ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python股票量化系统源码解析:从依赖管理到MySQL回测链路

Python股票量化系统源码解析:从依赖管理到MySQL回测链路 简介面向股票量化投资爱好者与入门开发者这份资源提供了完整的量化系统源码和配套学习教程帮助用户从环境配置开始一步步构建自己的Python股票分析平台。系统以MySQL作为数据存储基础通过运行win_main.py启动教程围绕市场数据获取、历史行情分析、交易策略编写、回测执行、结果评估与风险控制等核心模块展开并对主脚本的主要函数和运行逻辑进行了解读适合希望通过实战掌握量化策略开发全流程的投资者和Python学习者。资源共244个文件压缩包大小仅3.53MB其中py文件是系统核心策略逻辑pyc为编译生成的中间文件png、jpg、gif图片用于界面展示和流程示意json与ui文件承担参数配置与窗口布局css、html、md文件提供前端样式和说明文档整体分类明确便于按模块查找和修改代码。目前已有38人学习下载对于需要快速搭建本地量化实验环境、理解从策略设计到回测验证完整链路的学习者来说是一套轻量、实用的参考资源。1. 这套 Python 股票量化系统源码到底能做什么一个压缩包里塞着几个 CSS、一个setting.html、一个demo.html和win_main.py第一眼不像完整产品。但把它拆开会发现这是一条“环境准备 - 依赖安装 - MySQL 建库 - 主入口启动 - 策略回测”的完整链路适合刚把 Python 和 MySQL 装好、想知道量化系统代码怎么组织的人。对写过策略但没拆过工程的人也有价值能看到一个最小可用系统的边界在哪哪些模块必须自己补。下面按实际跑起来会遇到的顺序说先把pip install -r requirement.txt这一步说透。2. requirement.txt 依赖管理与 Python 环境搭建2.1 一份典型 requirement.txt 里有什么拿到源码包先别急着跑python win_main.py先看requirement.txt。这套系统里Python 版本和依赖库直接决定后面会不会出现兼容性问题。很多网上的 Python 安装教程只教你装解释器真正让新手卡住的往往是装完依赖之后 import 失败。按常见的量化工程来分类requirement.txt里的库大概会覆盖三块数据获取、数值计算和界面展示。第一块是数据源库比如akshare、tushare这类通过 HTTP 接口抓行情和财务数据的库。第二块是numpy、pandas量化策略本质上就是在做 DataFrame 的切片、重采样和滚动计算没有 pandas 整个系统跑不起来。第三块是 GUI 或 Web 框架因为压缩包里能看到setting.html、demo.html和一堆layui.css、layer.css样式文件说明前端展示部分多半是 Web 页面后端需要一个框架来渲染这些页面常见的是 Flask。另外还有pymysql或sqlalchemy用来连 MySQLmatplotlib用来画回测曲线。下面是按依赖作用划分的一张参考表依赖组常见库作用数据获取akshare / tushare拉取行情、财务、板块数据计算numpy / pandas序列计算、数据清洗、滚动窗口数据库pymysql / sqlalchemy连接 MySQL读写行情与策略结果展示flask / PyQt5渲染设置页和行情页可视化matplotlib / pyecharts画净值曲线、信号图这里把依赖拆成组是因为排错时能更快定位数据拿不到问题在数据源页面打不开问题在 Web 框架。2.2 一条命令装完并解决下载慢的问题安装依赖时直接用官方给的命令pip install -r requirement.txt如果机器上同时存在 Python 2 和 Python 3最好先用python -m pip install -r requirement.txt确保装到当前python解释器对应的 pip 里。官方源在国内经常很慢常见做法是追加清华镜像pip install -r requirement.txt -i https://pypi.tuna.tsinghua.edu.cn/simple --timeout 60这段命令里-i指定镜像源--timeout 60把网络超时时间拉长到 60 秒避免大包下载到一半断掉。如果某个包编译失败比如pandas在新版本 Python 下没有预编译 wheel可以先单独装编译依赖或者把 Python 降到系统支持更成熟的版本。requirement.txt里的版本号是开发者的锁版本不要随意改成最新的因为numpy和pandas的版本对相互之间的二进制接口要求很严格版本不匹配会直接出现numpy.dtype size changed这类只有重装才能解决的报错。2.3 依赖装完但 import 失败的三个原因依赖全部装完之后在项目根目录执行python -c import pandas, numpy, pymysql; print(ok)如果输出ok说明基础环境没问题。如果报错最常见的有三种第一种是当前命令行的工作目录不在项目根目录导致requirement.txt没被读对这时先cd到源码目录再装第二种是 pip 装到了系统全局环境而运行系统时用了虚拟环境两个环境不是同一个site-packages解决方法是创建虚拟环境后在同一个环境里安装和执行第三种是系统的 Python 版本太低比如 Python 3.6 跑新版 pandas 直接不支持这时要读一下源码里的说明或者看win_main.py顶部的版本判断代码。提示装依赖前先执行python --version确认解释器版本再执行which python或where python确认正在使用哪个解释器避免装了半天发现装到了另一个环境里。3. MySQL 用户与建库跑通前的第一道关卡3.1 为什么量化系统要有 MySQL量化系统的运行离不开历史数据和实盘数据。如果只做一次性计算用 CSV 文件也能凑合但策略要反复回测需要增量更新行情数据、保存每次回测参数和结果还得在多进程或多线程下同时读写CSV 很容易出现写冲突。MySQL 的 InnoDB 引擎支持行锁和事务适合这种场景。这套源码在说明里明确要求用户名root、密码88888888说明代码里很可能直接写死了一组数据库连接配置。也就是说本地 MySQL 的用户名密码必须和源码里的配置一致否则win_main.py启动后第一件事就是连接数据库失败。常见做法是创建专用数据库用户而不是直接用 root但这套资源已经指定了 root。为了不修改源码访问数据库的配置按它的要求把本地 root 密码调整成88888888是最省事的方式。如果电脑是专用的开发机没有其他业务系统依赖 root 密码直接改掉即可。3.2 root 用户与密码 88888888 的 SQL 配置先确认 MySQL 服务已经启动再使用 MySQL 客户端执行以下 SQL-- 本机已存在 root 时 ALTER USER rootlocalhost IDENTIFIED BY 88888888; -- 没有 root 或需要新建时 CREATE USER IF NOT EXISTS rootlocalhost IDENTIFIED BY 88888888; -- 建库 CREATE DATABASE IF NOT EXISTS stock_quant DEFAULT CHARACTER SET utf8mb4; FLUSH PRIVILEGES;第一行是修改已有 root 账号的密码第二行是兜底创建用户第三行建数据库。utf8mb4用来存中文股票名称比utf8支持更全。FLUSH PRIVILEGES重新加载权限表保证密码立即生效。如果 MySQL 是 Docker 启动的需要先进入容器再执行docker exec -it mysql bash -c mysql -uroot -p -e ALTER USER ...注意如果在 root 密码未知的情况下想重置密码需要修改 MySQL 配置跳过权限验证后重启这是另一个话题。这里只建议在干净开发机上操作。3.3 Python 连接 MySQL 的配置写法系统运行时需要读取 MySQL 里的历史数据也会把新的行情写回数据库。常见的数据库连接写法有两种直接用pymysql或者用sqlalchemy做 ORM。源码里更常见的是在win_main.py或config.py里维护一个连接参数对象import pymysql DB_CONFIG { host: 127.0.0.1, port: 3306, user: root, password: 88888888, database: stock_quant, charset: utf8mb4, autocommit: True, } def get_conn(): return pymysql.connect(**DB_CONFIG)这里把autocommit打开是为了避免写入数据后忘记commit()导致下次查询看不到。charset必须和建库时的字符集一致否则中文会出现乱码。如果源码里用sqlalchemy则连接串长这样from sqlalchemy import create_engine engine create_engine(mysqlpymysql://root:88888888127.0.0.1:3306/stock_quant?charsetutf8mb4)3.4 常见连接报错排查启动后如果控制台报Access denied for user rootlocalhost说明密码和源码预期不一致回到 3.2 重新设置。报Unknown database stock_quant说明建库失败或库名大小写不对。报Cant connect to MySQL server on 127.0.0.1说明 MySQL 服务没启动或者端口不是 3306。下面这张表可以直接对照报错片段原因处理Access denied用户名或密码不匹配执行 ALTER USER 重设密码Unknown database没有创建目标库执行 CREATE DATABASECant connect服务未启动或端口错误检查服务状态和配置端口Host not allowed用户只允许本机访问用 rootlocalhost 或授权网段提示改完 MySQL 权限后一定要FLUSH PRIVILEGES特别是通过mysql_install_db初始化过的环境。4. 从 win_main.py 看量化系统的主入口设计4.1 主入口脚本通常拆成哪四段win_main.py是整个系统的启动文件运行python win_main.py后控制台会加载配置、连接数据库、启动策略、最后弹出或拉起页面。常见的组织方式可以分成四段配置加载、环境检查、策略调度、页面服务。这样拆的优势是出问题时从日志能直接定位到是哪个环节失败。配置加载阶段会读取setting.html对应的配置项。看到setting.html和demo.html这两个文件可以判断系统的设置页和演示页是前后端一体的。后端把数据库连接、策略参数写入页面前端通过 layui 组件展示前端改动配置后再通过 HTTP 请求回写配置文件。也就是说setting.html不是静态页面而是和win_main.py有交互的模板。环境检查阶段会验证 MySQL 连接和关键目录是否存在。策略调度阶段创建线程池把选股、回测、信号生成放到不同线程里。页面服务阶段启动 Flask 或本地 HTTP server让浏览器能访问demo.html。如果这一层没起来页面能打开但数据是空的问题往往出在接口路由或数据库连接上而不是前端。4.2 setting.html 与 demo.html 在入口脚本里的作用拿demo.html来说它展示的是行情图、策略信号和回测结果。页面中的数据不会硬编码进 HTML而是通过后端接口动态填充。比如app.route(/api/kline) def kline(): code request.args.get(code, 600000) data load_kline_from_mysql(code) return jsonify(data)这里/api/kline接口接收股票代码从 MySQL 读取 K 线数据返回 JSON。前端拿到数据后用layui或echarts渲染成图表。也就是说前端页面是结果展示层数据全在数据库里运行win_main.py的目的之一就是把页面服务和数据接口拉起来。4.3 策略注册与启动流程一个能扩展的量化系统不会把所有策略都写在主脚本里而是在内存里建一张策略注册表每个策略对应一个执行函数。常见做法是STRATEGY_REGISTRY {} def register(name): def decorator(func): STRATEGY_REGISTRY[name] func return func return decorator register(ma_cross) def ma_cross(ctx): df load_data(ctx[code]) df[ma5] df[close].rolling(5).mean() df[ma20] df[close].rolling(20).mean() return df[ma5] - df[ma20]register装饰器把函数存进字典load_data从数据库取数据。主入口启动后遍历STRATEGY_REGISTRY逐个调用策略函数。这种方式的好处是加新策略只需要新增一个register函数不需要修改主入口逻辑。源码包的价值也在这里它不是只有一次回测而是给你一套可以持续加策略的框架。4.4 启动命令与参数验证运行系统的标准命令是官方给出的python win_main.py如果源码里定义了argparse启动参数也可以传入数据路径、回测周期等。比如import argparse parser argparse.ArgumentParser() parser.add_argument(--mode, choices[backtest, live], defaultbacktest) parser.add_argument(--code, default600000) args parser.parse_args()--mode控制是回测还是实盘模拟--code指定默认标代码。启动后观察日志如果出现Connected to MySQL和Strategy ma_cross loaded说明数据库和策略加载都成功。下面这张表是启动日志的关键检查点日志关键字含义Connected to MySQL数据库连接成功Strategy ma_cross loaded策略加载成功Serving on ...页面服务已启动ERROR / Traceback启动中断定位到对应阶段如果只看到页面启动没有策略加载日志就要回头查 4.3 的装饰器是否被执行。5. 把数据、回测和信号接成一条完整链路5.1 行情数据获取与 MySQL 缓存量化系统第一步是获取干净的行情数据。现在常用的免费数据源有akshare和tushare它们都支持通过 pip 安装。以 akshare 为例拉取个股日线并写入 MySQL 是这样的import akshare as ak import pymysql def update_daily(code600000): df ak.stock_zh_a_hist(symbolcode, perioddaily, adjustqfq) df.columns [date, code, open, close, high, low, volume, amount, amplitude, pct_chg, change, turnover] conn pymysql.connect(host127.0.0.1, userroot, password88888888, databasestock_quant, charsetutf8mb4) for row in df.itertuples(): sql INSERT INTO kline (code,date,open,close,high,low,volume) VALUES (%s,%s,%s,%s,%s,%s,%s) ON DUPLICATE KEY UPDATE close%s conn.cursor().execute(sql, (code, row.date, row.open, row.close, row.high, row.low, row.volume, row.close)) conn.commit() conn.close()这段代码先把数据源的列名统一然后逐行写入kline表。ON DUPLICATE KEY UPDATE依赖(code, date)的唯一索引重复下载时只更新行情不会产生重复记录。表结构建议这样建CREATE TABLE IF NOT EXISTS kline ( id BIGINT AUTO_INCREMENT PRIMARY KEY, code VARCHAR(10) NOT NULL, date DATE NOT NULL, open DECIMAL(10,2), high DECIMAL(10,2), low DECIMAL(10,2), close DECIMAL(10,2), volume BIGINT, UNIQUE KEY uk_code_date (code, date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;唯一索引是这里的关键没有它ON DUPLICATE KEY UPDATE就无效数据会越攒越多。另外DECIMAL(10,2)比FLOAT更适合存价格避免浮点精度问题。5.2 双均线策略回测实现有了数据就可以写最简单的双均线策略。逻辑是5 日均线上穿 20 日均线买入下穿卖出。回测代码如下import pandas as pd def backtest(df, fast5, slow20): df df.sort_values(date) df[ma_fast] df[close].rolling(fast).mean() df[ma_slow] df[close].rolling(slow).mean() df[position] 0 df.loc[df[ma_fast] df[ma_slow], position] 1 df[position] df[position].shift(1).fillna(0) df[ret] df[close].pct_change().fillna(0) df[strategy_ret] df[position] * df[ret] df[nav] (1 df[strategy_ret]).cumprod() return dfshift(1)非常关键它模拟“信号出现后第二天开盘才交易”避免用到当天收盘价形成未来函数。把position后移一天再用收益乘仓位得到的是实际可实现的策略收益。cumprod累计净值用于计算最终收益。5.3 回测指标与参数选择的边界回测结果不能只看最后赚不赚钱至少要算几个指标总收益率、年化收益率、最大回撤、夏普比率、胜率。下面是评估表模板指标计算方式参考意义总收益率nav[-1] - 1衡量整体盈利年化收益率nav[-1] ** (252/len(df)) - 1换算成一年最大回撤min(nav / cummax(nav) - 1)衡量风险夏普比率mean(strategy_ret)/std(strategy_ret) * sqrt(252)收益风险比胜率sum(strategy_ret0)/count0交易质量双均线参数 5/20 只是默认值换到不同股票上表现差距很大。调参时要注意过拟合在历史数据上反复试参数总能找到“好看”的组合但换一段行情就会失效。所以回测至少应该分成样本内和样本外两段样本内选参数样本外验证稳定性。这也是为什么这套系统把回测放在源码里而不是只给结论因为你需要自己动手验证参数边界。6. 二次开发前的验证从日志、单元测试到可视化检查6.1 先跑通最小闭环改代码前先把主线跑通。MySQL 能查、依赖能 import、win_main.py能启动这三个条件缺一个都只能算没跑通。最小闭环是运行一次 Python 连接语句能连上数据库再运行系统不要一上来就改策略否则环境问题和策略问题混在一起日志都看不懂。先确认数据库和页面服务正常再谈策略。6.2 给策略函数加单元测试改完ma_cross后最稳的验证是给它写一个测试用例输入一个已知的 DataFrame断言仓位序列符合预期import unittest import pandas as pd class TestMaCross(unittest.TestCase): def test_position_shift(self): df pd.DataFrame({close: [1,2,3,4,5,6,7,8,9,10], date: pd.date_range(2024-01-01, periods10)}) result backtest(df, fast2, slow4) self.assertTrue(result[position].iloc[-1] in (0, 1)) self.assertFalse(result[ma_fast].isna().all())测试里重点检查position是否被后移以及均线列是否都生成。unittest是标准库不需要额外安装直接执行python -m unittest就能跑这个用例。6.3 用真实数据画一次信号图最后把数据从 MySQL 读出来画图确认信号是出现在价格图上合理的位置import matplotlib.pyplot as plt def plot_signal(df): plt.figure(figsize(12, 6)) plt.plot(df[date], df[close], labelclose) buy df[df[position].diff() 1] sell df[df[position].diff() -1] plt.scatter(buy[date], buy[close], marker^, colorred, labelbuy) plt.scatter(sell[date], sell[close], markerv, colorblue, labelsell) plt.legend() plt.show()position.diff() 1表示仓位从 0 变成 1对应买入信号diff() -1对应卖出信号。如果图中买入点出现在明显下跌段就要回查数据源是否存在前复权缺失。完成这一步二次开发的验证闭环就完整了依赖、数据库、策略、回测、可视化全部对齐。本文还有配套的精品资源点击获取
返回列表