Python量化选股工具开发:从数据获取到策略回测实战
最近在做一个量化策略回测项目时需要快速筛选出符合特定财务和技术条件的股票。手动翻看几千只股票的数据效率低不说还容易看花眼。于是我花了三天时间用 Python 写了一个自动化选股工具。当第一次跑出结果看到那些被筛选出来的股票组合在历史回测中的表现时确实有点“惊了”——原来通过一些简单的规则组合真的能初步过滤掉大量“噪音”找到潜在的机会池。本文就将这个工具的完整开发思路、代码实现以及使用心得分享出来。无论你是对量化投资感兴趣的初学者还是想为自己的策略增加一个自动化筛选环节的开发者都能从零开始跟着本文一步步搭建起属于自己的 Python 选股工具。文末会提供完整的、可运行的源码。1. 选股工具的核心思路与准备工作在开始敲代码之前我们需要明确工具要做什么以及需要哪些“原材料”。1.1 工具要解决什么问题传统选股可能依赖于经验或感觉而我们的工具旨在实现自动化代替人工快速遍历全市场股票。规则化将模糊的选股标准如“业绩好”、“趋势强”转化为具体的、可量化的规则如“市盈率小于30”、“收盘价在20日均线之上”。可回溯基于历史数据运行可以验证规则在过去是否有效。可扩展规则可以方便地增加、修改或组合。1.2 技术栈与数据源选择编程语言Python。因其在数据分析pandas, numpy和可视化matplotlib领域的强大生态而成为不二之选。核心库pandasnumpy: 数据处理与分析基石。akshare: 一个免费、开源的财经数据接口库可以获取到A股行情、基本面等数据非常适合个人开发者和小型项目。注意数据接口有变更风险本文代码基于某个稳定版本使用时请关注akshare官方文档matplotlib/seaborn: 用于简单的数据可视化例如绘制选中股票的股价走势。开发环境任何你熟悉的Python环境即可如PyCharm、VSCode、Jupyter Notebook。建议使用Python 3.8及以上版本。数据源本文示例使用akshare。你需要理解任何免费数据源都可能存在延迟、误差或接口变动用于学习和小规模回测没问题但若用于实盘务必寻求更稳定、更实时的数据源并进行充分验证。1.3 环境搭建与依赖安装首先确保你的Python环境已就绪。然后通过pip安装必要的库。建议创建一个新的虚拟环境。# 创建并激活虚拟环境以venv为例 python -m venv stock_env # Windows: stock_env\Scripts\activate # Linux/Mac: source stock_env/bin/activate # 安装核心依赖 pip install pandas numpy akshare matplotlib如果安装akshare较慢或遇到问题可以使用国内镜像源pip install pandas numpy akshare matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple2. 数据获取构建选股的基础数据库没有数据一切分析都是空谈。我们的第一步是学会如何获取并组织股票数据。2.1 获取股票基础信息列表我们需要知道市场上有哪些股票。akshare提供了接口获取A股列表。import akshare as ak # 获取沪深京A股列表 stock_info_a_code_name_df ak.stock_info_a_code_name() print(f“获取到A股数量{len(stock_info_a_code_name_df)}”) print(stock_info_a_code_name_df.head()) # 保存到本地CSV避免每次运行都重新下载可选 stock_info_a_code_name_df.to_csv(“stock_basic_list.csv”, indexFalse, encoding‘utf-8-sig’)运行后你会得到一个包含股票代码和股票名称的DataFrame。股票代码是后续获取详细数据的钥匙。2.2 获取个股历史行情数据行情数据是技术分析的基础。我们写一个函数来获取单只股票的历史日K线数据。import pandas as pd def get_stock_daily_data(stock_code, start_date“20230101”, end_date“20231231”): “”” 获取单只股票的日线行情数据 :param stock_code: 股票代码带交易所前缀例如 ‘sh600000’ 或 ‘sz000001’ :param start_date: 开始日期格式 ‘YYYYMMDD’ :param end_date: 结束日期格式 ‘YYYYMMDD’ :return: pandas DataFrame “”” try: # 注意ak.stock_zh_a_hist 需要标准代码如 ‘600000’ 不需要前缀 # 但我们需要区分市场这里假设传入的是 ‘600000’ 或 ‘000001’ # 实际使用中需要根据代码前几位判断市场这里做简单处理 if stock_code.startswith(‘6’): symbol stock_code ‘.SH’ elif stock_code.startswith(‘0’) or stock_code.startswith(‘3’): symbol stock_code ‘.SZ’ else: print(f“无法识别股票代码市场{stock_code}”) return None # 使用 ak.stock_zh_a_hist 获取数据注意参数名可能随版本变化 df ak.stock_zh_a_hist(symbolsymbol, period“daily”, start_datestart_date, end_dateend_date, adjust“qfq”) # adjust“qfq” 表示前复权保证价格可比性 if df is not None and not df.empty: # 重命名列使其更易读 df.columns [‘日期’ ‘开盘’ ‘收盘’ ‘最高’ ‘最低’ ‘成交量’ ‘成交额’ ‘振幅’ ‘涨跌幅’ ‘涨跌额’ ‘换手率’] df[‘日期’] pd.to_datetime(df[‘日期’]) df.set_index(‘日期’ inplaceTrue) df.sort_index(inplaceTrue) # 按日期升序排列 return df else: print(f“未获取到股票 {stock_code} 的数据”) return None except Exception as e: print(f“获取股票 {stock_code} 数据时出错{e}”) return None # 测试获取贵州茅台的数据 df_mt get_stock_daily_data(‘600519’ ‘20240101’ ‘20240601’) if df_mt is not None: print(df_mt.head()) print(f“数据形状{df_mt.shape}”)关键点说明复权adjust“qfq”前复权至关重要。它消除了分红、送股等事件对股价造成的断层使得历史价格序列连续可比是进行任何技术分析的前提。错误处理网络请求可能失败接口可能变化所以用try-except包裹并检查返回数据是否为空。数据缓存频繁请求大量股票数据可能被限制或速度慢。在实际开发中应考虑将数据缓存到本地数据库如SQLite或文件中下次直接读取。2.3 获取基本面数据以市盈率为例基本面数据更新频率较低季度或年度我们可以一次性获取全市场的最新数据。def get_basic_financial_data(): “”” 获取A股最新基本面数据示例市盈率、市净率 :return: pandas DataFrame “”” try: # 获取沪深京A股实时行情数据其中包含一些基本面字段 # 注意不同接口返回的字段名可能不同这里是一个示例 df ak.stock_zh_a_spot_em() # 筛选我们关心的列代码、名称、市盈率动态、市净率 # 需要根据 ak.stock_zh_a_spot_em 返回的实际列名调整 if ‘代码’ in df.columns and ‘名称’ in df.columns: # 假设列名是 ‘市盈率-动态’ 和 ‘市净率’ 请根据打印出的列名确认 print(“可用列” df.columns.tolist()) # 例如选择以下列 selected_cols [‘代码’ ‘名称’ ‘最新价’ ‘市盈率-动态’ ‘市净率’] if all(col in df.columns for col in selected_cols): basic_df df[selected_cols].copy() # 重命名 basic_df.columns [‘code’ ‘name’ ‘price’ ‘pe_ratio’ ‘pb_ratio’] # 将字符串类型的数值转换为浮点数处理‘-’等无效值 basic_df[‘pe_ratio’] pd.to_numeric(basic_df[‘pe_ratio’] errors‘coerce’) basic_df[‘pb_ratio’] pd.to_numeric(basic_df[‘pb_ratio’] errors‘coerce’) return basic_df return None except Exception as e: print(f“获取基本面数据出错{e}”) return None # 测试 basic_df get_basic_financial_data() if basic_df is not None: print(basic_df.head()) print(f“获取到 {len(basic_df)} 条基本面数据”)注意基本面数据接口和字段名变化相对频繁。运行上述代码后请先查看打印出的df.columns确认正确的市盈率、市净率等字段名然后修改selected_cols列表。pd.to_numeric(..., errors‘coerce’)会将无效字符串如‘-’转换为NaN便于后续过滤。3. 定义并实现选股规则策略这是工具的核心。我们将选股逻辑封装成一个个独立的“过滤器”Filter每个过滤器负责检查股票是否满足单一条件。最后可以灵活组合这些过滤器。3.1 规则一市盈率过滤器价值型筛选市盈率在合理范围内的股票排除亏损PE为负和估值过高的股票。def filter_by_pe(basic_df, pe_max30, pe_min0): “”” 根据市盈率过滤股票 :param basic_df: 包含 ‘pe_ratio’ 列的基本面DataFrame :param pe_max: 市盈率上限 :param pe_min: 市盈率下限通常为0排除负值 :return: 满足条件的股票代码列表 “”” if basic_df is None or ‘pe_ratio’ not in basic_df.columns: print(“基本面数据缺失或没有市盈率字段”) return [] # 过滤PE在 [pe_min, pe_max] 区间内且不是NaN condition (basic_df[‘pe_ratio’] pe_min) (basic_df[‘pe_ratio’] pe_max) (basic_df[‘pe_ratio’].notna()) filtered_codes basic_df.loc[condition ‘code’].tolist() print(f“市盈率过滤器从 {len(basic_df)} 只股票中筛选出 {len(filtered_codes)} 只。”) return filtered_codes3.2 规则二价格均线过滤器趋势型筛选当前价格位于某条均线之上的股票例如“收盘价高于20日均线”这通常被认为是短期趋势向上的信号。def filter_by_ma(stock_code, daily_df, ma_days20): “”” 判断单只股票是否满足均线条件 :param stock_code: 股票代码 :param daily_df: 该股票的日线DataFrame :param ma_days: 均线周期 :return: True 如果满足条件最新收盘价 MA否则 False “”” if daily_df is None or daily_df.empty: return False # 计算移动平均线 daily_df[‘ma’] daily_df[‘收盘’].rolling(windowma_days).mean() # 获取最新的数据点 latest_data daily_df.iloc[-1] # 检查最新收盘价是否高于均线并且均线值不是NaN确保有足够数据计算 if pd.notna(latest_data[‘ma’]) and latest_data[‘收盘’] latest_data[‘ma’]: return True else: return False # 批量应用均线过滤器 def batch_filter_by_ma(code_list, start_date, end_date, ma_days20): “”” 批量过滤满足均线条件的股票 :param code_list: 待筛选的股票代码列表 :param ma_days: 均线周期 :return: 满足条件的股票代码列表 “”” passed_codes [] total len(code_list) for i, code in enumerate(code_list): # 添加简单进度提示 if i % 100 0: print(f“均线过滤进度{i}/{total}”) df get_stock_daily_data(code, start_date, end_date) if filter_by_ma(code, df, ma_days): passed_codes.append(code) print(f“均线过滤器从 {total} 只股票中筛选出 {len(passed_codes)} 只。”) return passed_codes注意batch_filter_by_ma函数会为每只股票请求历史数据如果股票数量很多运行会非常慢。这是第一个性能瓶颈。生产环境中需要优化例如使用本地缓存的数据或者采用异步请求。3.3 规则三量价齐升过滤器动能型筛选“近期成交量显著放大且股价上涨”的股票。一个简单的实现今日成交量大于过去N日平均成交量的M倍且今日股价上涨。def filter_by_volume_price(stock_code, daily_df, avg_days5, volume_ratio1.5): “”” 量价齐升过滤器 :param stock_code: 股票代码 :param daily_df: 日线数据 :param avg_days: 计算平均成交量的周期 :param volume_ratio: 今日成交量需要超过均量的倍数 :return: True 如果满足条件 “”” if daily_df is None or len(daily_df) avg_days 1: return False # 计算过去 avg_days 日的平均成交量不包括今日 avg_volume daily_df[‘成交量’].iloc[-(avg_days1):-1].mean() latest_data daily_df.iloc[-1] # 条件今日成交量 平均成交量 * volume_ratio且今日股价上涨涨跌幅0 condition (latest_data[‘成交量’] avg_volume * volume_ratio) and (latest_data[‘涨跌幅’] 0) return condition3.4 规则组合器我们可以将多个过滤器串联“与”逻辑或并联“或”逻辑。这里实现一个简单的串联组合必须通过所有指定的过滤器。def composite_filter(code_list, basic_df, start_date, end_date, filters_config): “”” 组合过滤器 :param code_list: 初始股票池 :param basic_df: 基本面数据 :param start_date: 行情开始日期 :param end_date: 行情结束日期 :param filters_config: 过滤器配置字典 例如{‘pe’: {‘pe_max’: 30, ‘pe_min’: 5}, ‘ma’: {‘ma_days’: 20}, ‘volume’: {‘avg_days’: 5, ‘volume_ratio’: 1.8}} :return: 通过所有过滤器的股票代码列表 “”” current_pool code_list.copy() print(f“初始股票池数量{len(current_pool)}”) # 1. 应用基本面过滤器无需行情数据速度快 if ‘pe’ in filters_config: pe_codes filter_by_pe(basic_df, **filters_config[‘pe’]) # 取交集当前股票池中且满足PE条件的 current_pool list(set(current_pool) set(pe_codes)) print(f“应用PE过滤器后剩余{len(current_pool)}”) # 2. 应用需要行情数据的过滤器 # 为了效率先获取这些股票的历史数据可考虑并行或缓存优化 passed_codes [] total len(current_pool) for i, code in enumerate(current_pool): if i % 50 0: print(f“行情数据过滤进度{i}/{total}”) daily_df get_stock_daily_data(code, start_date, end_date) if daily_df is None: continue passed True # 检查均线条件 if ‘ma’ in filters_config and passed: if not filter_by_ma(code, daily_df, **filters_config[‘ma’]): passed False # 检查量价条件 if ‘volume’ in filters_config and passed: if not filter_by_volume_price(code, daily_df, **filters_config[‘volume’]): passed False # 可以继续添加其他过滤器... if passed: passed_codes.append(code) print(f“组合过滤完成。最终选出 {len(passed_codes)} 只股票。”) return passed_codes4. 完整实战运行选股并分析结果现在我们将所有模块组合起来进行一次完整的选股演练。4.1 主程序流程创建一个main.py文件整合所有步骤。# main.py import pandas as pd import akshare as ak from datetime import datetime, timedelta import time # 导入我们上面写的函数假设它们在一个叫 stock_filters.py 的文件里 # 为了演示我们把所有函数放在同一个文件里运行 # 这里直接复制了上面的函数定义实际项目中应分模块 def main(): print(“ Python选股工具开始运行 ”) start_time time.time() # 步骤1设置参数 end_date datetime.now().strftime(“%Y%m%d”) start_date (datetime.now() - timedelta(days180)).strftime(“%Y%m%d”) # 最近180天数据 print(f“分析周期{start_date} 至 {end_date}”) # 步骤2获取基础数据 print(“\n1. 获取A股基础列表...”) stock_list_df ak.stock_info_a_code_name() initial_codes stock_list_df[‘code’].tolist() print(f“获取到初始股票池{len(initial_codes)} 只股票”) print(“\n2. 获取基本面数据市盈率、市净率...”) basic_df get_basic_financial_data() # 调用之前定义的函数 if basic_df is None: print(“获取基本面数据失败退出。”) return # 步骤3定义选股策略过滤器组合 my_strategy { ‘pe’: {‘pe_max’: 40, ‘pe_min’: 5}, # 市盈率介于5到40倍 ‘ma’: {‘ma_days’: 20}, # 股价在20日均线之上 ‘volume’: {‘avg_days’: 10, ‘volume_ratio’: 1.5} # 量能是10日均量的1.5倍以上且上涨 } # 步骤4执行组合过滤 print(“\n3. 开始执行组合选股策略...”) selected_codes composite_filter( code_listinitial_codes, basic_dfbasic_df, start_datestart_date, end_dateend_date, filters_configmy_strategy ) # 步骤5输出和保存结果 print(“\n4. 选股结果”) if selected_codes: selected_stocks stock_list_df[stock_list_df[‘code’].isin(selected_codes)] print(selected_stocks[[‘code’ ‘name’]].to_string(indexFalse)) # 保存到文件 result_file f“selected_stocks_{end_date}.csv” selected_stocks.to_csv(result_file, indexFalse, encoding‘utf-8-sig’) print(f“\n结果已保存到文件{result_file}”) # 步骤6可选简单分析选中股票 print(“\n5. 对选中股票进行简要分析...”) # 例如计算平均市盈率 if ‘pe_ratio’ in basic_df.columns: selected_pe basic_df[basic_df[‘code’].isin(selected_codes)][‘pe_ratio’] print(f“选中股票的平均市盈率{selected_pe.mean():.2f}”) print(f“选中股票的市盈率中位数{selected_pe.median():.2f}”) else: print(“没有股票满足所有选股条件。”) elapsed_time time.time() - start_time print(f“\n 选股完成总耗时{elapsed_time:.2f} 秒 ”) if __name__ “__main__”: main()4.2 运行结果与初步分析当你运行这个脚本后控制台会输出类似以下信息 Python选股工具开始运行 分析周期20231001 至 20240601 1. 获取A股基础列表... 获取到初始股票池5123 只股票 2. 获取基本面数据市盈率、市净率... 可用列 [‘代码’ ‘名称’ ‘最新价’ ‘涨跌幅’ ‘涨跌额’ ‘成交量’ ‘成交额’ ‘振幅’ ‘最高’ ‘最低’ ‘今开’ ‘昨收’ ‘量比’ ‘换手率’ ‘市盈率-动态’ ‘市净率’ ‘总市值’ ‘流通市值’ ‘涨速’ ‘5分钟涨跌’ ‘60日涨跌幅’ ‘年初至今涨跌幅’] 获取到 5123 条基本面数据 3. 开始执行组合选股策略... 初始股票池数量5123 应用PE过滤器后剩余1890 行情数据过滤进度0/1890 行情数据过滤进度50/1890 ... 组合过滤完成。最终选出 47 只股票。 4. 选股结果 code name 0 000001 平安银行 1 000002 万科A ... 结果已保存到文件selected_stocks_20240601.csv 5. 对选中股票进行简要分析... 选中股票的平均市盈率18.34 选中股票的市盈率中位数16.72 选股完成总耗时328.47 秒 “惊了”的时刻当你第一次看到从5000多只股票中通过几条简单的量化规则筛选出几十只股票并且它们的平均市盈率、技术形态都符合你预设的“审美”时你会感受到规则和自动化的力量。你可以立即对这些股票进行更深入的分析或者将其作为进一步策略研究的股票池效率提升巨大。4.3 可视化验证可选我们可以随机挑几只选中的股票绘制其股价与均线走势直观感受筛选结果。import matplotlib.pyplot as plt def plot_stock_with_ma(stock_code, start_date, end_date, ma_days20): “””绘制股票价格及移动平均线””” df get_stock_daily_data(stock_code, start_date, end_date) if df is None: return df[‘ma’] df[‘收盘’].rolling(windowma_days).mean() plt.figure(figsize(12, 6)) plt.plot(df.index, df[‘收盘’] label‘Close Price’ linewidth1.5) plt.plot(df.index, df[‘ma’] labelf‘MA{ma_days}’ linestyle‘--’ linewidth1.5) plt.title(f‘{stock_code} - Price MA{ma_days}’) plt.xlabel(‘Date’) plt.ylabel(‘Price’) plt.legend() plt.grid(True, linestyle‘:’ alpha0.7) plt.show() # 假设 selected_codes 是之前选出的股票列表 if selected_codes: # 画前3只股票的图 for code in selected_codes[:3]: plot_stock_with_ma(code, start_date, end_date, ma_days20) time.sleep(1) # 避免请求过快5. 常见问题与优化方案避坑指南在开发和运行过程中你肯定会遇到一些问题。以下是一些典型问题及解决思路。5.1 数据获取失败或速度慢问题现象可能原因解决思路ak.stock_zh_a_hist报错或返回空1. 接口已更新参数名或URL变化。2. 股票代码格式不对。3. 网络问题。1. 查看akshare最新官方文档或GitHub Issues。2. 打印请求的URL和参数手动在浏览器测试。3. 使用try-except捕获异常并跳过该股票。获取全市场数据耗时极长1. 循环请求单线程串行。2. 网络延迟。1.使用本地缓存第一次获取后存入SQLite或.pkl文件后续增量更新。2.使用并发使用concurrent.futures.ThreadPoolExecutor进行多线程请求注意目标网站的反爬策略。3.减少请求频率在循环中添加time.sleep(0.1)。基本面数据字段名对不上akshare接口字段名可能随版本更新。打印df.columns根据实际输出调整代码中的列名。代码示例简单的数据缓存import os import pickle def get_daily_data_with_cache(stock_code, start_date, end_date, cache_dir‘./data_cache’): “””带缓存的数据获取函数””” os.makedirs(cache_dir, exist_okTrue) cache_file os.path.join(cache_dir, f“{stock_code}_{start_date}_{end_date}.pkl”) # 如果缓存存在且未过期例如判断文件存在且修改时间在当天 if os.path.exists(cache_file): file_mtime datetime.fromtimestamp(os.path.getmtime(cache_file)) if file_mtime.date() datetime.now().date(): # 当天缓存有效 with open(cache_file, ‘rb’) as f: print(f“从缓存加载 {stock_code} 数据”) return pickle.load(f) # 缓存不存在或已过期重新请求 print(f“请求 {stock_code} 数据”) df get_stock_daily_data(stock_code, start_date, end_date) if df is not None: with open(cache_file, ‘wb’) as f: pickle.dump(df, f) return df5.2 选股逻辑的常见陷阱未来函数这是量化策略中最致命的错误。例如在2024年1月1日使用了2024年1月2日才能知道的数据如当日收盘价来做决策。我们的代码中get_stock_daily_data获取的是历史数据在回测时必须确保在每一个模拟的“交易日”只能使用该日期及之前的数据。本文示例在批量过滤时使用了统一的end_date这相当于站在end_date这个时点回头看是符合回测逻辑的。但如果你要模拟每日滚动选股则需要重构代码按时间点逐日计算。幸存者偏差我们使用的stock_info_a_code_name()获取的是当前上市的股票列表。这意味着已经退市的股票不在其中。用这个列表回测过去会天然地排除掉那些后来退市的“差生”导致回测结果过于乐观。更严谨的做法是获取历史某一天的全体股票列表。过拟合如果你不断调整pe_max、ma_days等参数直到它们在历史数据上表现完美那么这个策略很可能已经“过拟合”了历史噪音在未来失效。应对方法是将历史数据分为训练集和测试集用训练集开发策略用测试集验证或者进行交叉验证。5.3 性能优化建议当股票数量多、历史周期长时循环获取数据并计算会成为瓶颈。向量化计算尽可能使用pandas/numpy的向量化操作避免Python层面的循环。例如计算所有股票的均线条件可以尝试先构建一个包含所有股票历史数据的大DataFrame多级索引然后一次性计算。并行处理使用concurrent.futures或multiprocessing模块并行处理股票数据获取和指标计算。使用专业回测框架对于更复杂的策略建议迁移到backtrader、zipline或qstrader等专业框架它们提供了更高效的数据处理和事件驱动回测引擎。6. 工程化与最佳实践将这个脚本升级为一个可维护、可扩展的工具你需要考虑以下几点6.1 项目结构stock_screener/ ├── data/ # 数据缓存目录 ├── src/ # 源代码 │ ├── __init__.py │ ├── data_fetcher.py # 数据获取模块 │ ├── filters.py # 选股过滤器模块 │ ├── screener.py # 核心筛选引擎 │ └── utils.py # 工具函数缓存、日志等 ├── config.yaml # 配置文件策略参数、数据源等 ├── main.py # 主程序入口 ├── requirements.txt # 项目依赖 └── README.md # 项目说明6.2 配置化管理将策略参数、数据源地址、缓存设置等写入配置文件如config.yaml避免硬编码。# config.yaml data: start_date: “20240101” end_date: “20240601” cache_enabled: true cache_dir: “./data” strategy: filters: pe: enabled: true pe_max: 40 pe_min: 5 ma: enabled: true ma_days: 20 volume: enabled: true avg_days: 10 volume_ratio: 1.5在主程序中用yaml库加载配置。6.3 日志记录使用Python内置的logging模块替代print可以方便地控制日志级别、输出到文件等。import logging logging.basicConfig( levellogging.INFO, format‘%(asctime)s - %(name)s - %(levelname)s - %(message)s’, handlers[ logging.FileHandler(‘screener.log’), logging.StreamHandler() ] ) logger logging.getLogger(__name__) # 使用 logger 代替 print logger.info(f“获取到初始股票池{len(initial_codes)} 只股票”) logger.warning(“获取股票 %s 数据失败跳过。” stock_code)6.4 策略的扩展性本文展示了PE、均线、量价三个过滤器。你可以很容易地添加更多技术指标MACD金叉、RSI超卖、布林带突破等。可以集成TA-Lib库来计算复杂指标。基本面指标营收增长率、净利润率、ROE、负债率等。需要从财报接口获取数据。市场情绪换手率、资金流向、龙虎榜数据等。自定义规则例如“所属行业为新能源”、“非ST股票”。只需在filters.py中定义新的过滤函数并在composite_filter函数中添加相应的判断逻辑即可。6.5 关于实盘的严肃警告本文构建的工具仅适用于学习、研究和初步的策略探索绝对不可直接用于实盘交易。原因包括数据质量与延迟免费数据可能存在错误、缺失或显著延迟。交易成本策略未考虑佣金、印花税、滑点实际成交价与预期价的偏差这些会极大侵蚀利润。市场冲击小资金模拟交易不影响市场大资金实盘买卖本身就会影响价格。策略失效任何历史有效的策略都可能在未来失效。系统风险网络、程序BUG、交易所规则变化等。如果你有实盘需求必须在专业量化平台进行严格的回测和模拟盘交易并充分理解所有风险。7. 总结与后续学习方向通过这三天的开发我们完成了一个具备基本功能的Python选股工具。它实现了从数据获取、规则定义、组合筛选到结果输出的完整流程。这个工具的价值不在于提供一个“必胜策略”而在于提供了一个可验证、可迭代、可扩展的量化研究框架。你接下来可以做什么丰富数据源接入更多维度的数据如财务报告、行业分类、宏观经济指标。开发更复杂的策略学习经典量化策略如双均线、海龟交易法、均值回归并用代码实现。引入回测引擎计算策略的历史收益、夏普比率、最大回撤等关键指标科学评价策略好坏。优化性能使用数据库存储数据利用并行计算加速。增加可视化用Plotly或PyQt制作图形化界面让操作更直观。学习机器学习尝试用线性回归、决策树等模型来预测股价或生成选股信号。量化投资是一个交叉领域结合了编程、金融和数学。这个选股工具是你踏入这个领域的第一块坚实的垫脚石。最重要的是通过亲手搭建它你不仅学会了如何使用Python处理金融数据更关键的是建立了“逻辑化、规则化、可验证”的投资分析思维。