如何加速Python股票筛选器代码?优化慢运行选股脚本
优化Python股票筛选代码的运行效率
我有一段可正常运行但速度极慢的Python股票筛选代码,每只股票的信息处理耗时约10秒,若处理2000只股票则需数小时。作为编程新手,我希望能对代码进行修改以提升运行效率,以下是我的代码:
import yfinance as yf import pandas as pd import matplotlib.pyplot as plt from matplotlib.widgets import Cursor import numpy as np from IPython.display import display from datetime import datetime from currency_converter import CurrencyConverter c = CurrencyConverter() tickers = ['EDF.PA', 'anto.l', 'meta', 'iag.l', 'PFE','PKG','0700.hk', 'mbg.de'] df = pd.DataFrame({'Company': [''], 'P/S': [''],'P/E': [''],'P/B': [''],'Profit Margin(%)': [''],'ROE(%)': [''],'Current Ratio': [''],}) row = 0 for ticker in tickers: ticker = yf.Ticker(ticker) quarterly_financials = ticker.quarterly_financials quarterly_financials['TTM'] = quarterly_financials.sum(axis=1) quarterly_balance_sheet = ticker.quarterly_balance_sheet quarterly_balance_sheet = quarterly_balance_sheet.iloc[:, [3, 2, 1, 0]] quarterly_balance_sheet.fillna('none', inplace=True) TTM = quarterly_balance_sheet.mean(axis=1) TTM.fillna('none', inplace=True) try: balance_sheet = ticker.balance_sheet balance_sheet = balance_sheet.iloc[:, [3, 2, 1, 0]] except Exception: pass #...Data... try: # Changes value of 'currency from 'GBp' to 'GBP' if ticker.info['currency'] == 'GBp': ticker.info['currency'] = 'GBP' # Makes sure market cap currency matches business currency if ticker.info['currency'] == ticker.info['financialCurrency']: MarketCap = ticker.info['marketCap'] elif ticker.info['currency'] != ticker.info['financialCurrency']: MarketCap = ticker.info['marketCap'] x = ticker.info['currency'] y = ticker.info['financialCurrency'] # Converts the market cap from, x (market cap currency), to y (financial currency), using the last available rate MarketCap = c.convert(MarketCap, x, y) except: MarketCap = 0 try: TotalRevenue = (quarterly_financials.loc['Total Revenue'].iloc[-1]) except: TotalRevenue = 0 try: NetIncome = (quarterly_financials.loc['Net Income'].iloc[-1]) except: NetIncome = 0 try: TotalCurrentAssets = (quarterly_balance_sheet.loc['Total Current Assets'].iloc[-1]) TotalCurrentLiabilities = (quarterly_balance_sheet.loc['Total Current Liabilities'].iloc[-1]) TotalCurrentAssets/TotalCurrentLiabilities except: try: TotalCurrentAssets = (TTM.loc['Total Current Assets']) TotalCurrentLiabilities = (TTM.loc['Total Current Liabilities']) TotalCurrentAssets/TotalCurrentLiabilities except: try: TotalCurrentAssets = (balance_sheet.loc['Total Current Assets'].iloc[-1]) TotalCurrentLiabilities = (balance_sheet.loc['Total Current Liabilities'].iloc[-1]) TotalCurrentAssets/TotalCurrentLiabilities except: TotalCurrentAssets = 0 TotalCurrentLiabilities = 0 print("Current Assets and Liabilities 4") try: TotalAssets = (quarterly_balance_sheet.loc['Total Assets'].iloc[-1]) TotalLiabilities = (quarterly_balance_sheet.loc['Total Liab'].iloc[-1]) TotalAssets - TotalLiabilities except: try: TotalAssets = (TTM.loc['Total Assets'].iloc[-1]) TotalLiabilities = (TTM.loc['Total Liab'].iloc[-1]) TotalAssets - TotalLiabilities except: try: TotalAssets = (balance_sheet.loc['Total Assets'].iloc[-1]) TotalLiabilities = (balance_sheet.loc['Total Liab'].iloc[-1]) TotalAssets - TotalLiabilities except: TotalAssets = 0 TotalLiabilities = 0 #...Creates Stock Screen df... try: df.at[row, 'Company'] = ticker.info['longName'] except: df.at[row, 'Company'] = '' try: df.at[row, 'P/S'] = round((MarketCap/TotalRevenue), 2) except: df.at[row, 'P/S'] = 0 try: df.at[row, 'P/E'] = round((MarketCap / NetIncome), 2) except: df.at[row, 'P/E'] = 0 try: df.at[row, 'P/B'] = round((MarketCap / (TotalAssets - TotalLiabilities)), 2) except: df.at[row, 'P/B'] = 0 try: df.at[row, 'Profit Margin(%)'] = round((NetIncome / TotalRevenue)*100, 2) except: df.at[row, 'Profit Margin(%)'] = 0 try: df.at[row, 'ROE(%)'] = round((NetIncome / (TotalAssets - TotalLiabilities))*100, 2) except: df.at[row, 'ROE(%)'] = 0 try: df.at[row, 'Current Ratio'] = TotalCurrentAssets/TotalCurrentLiabilities except: df.at[row, 'Current Ratio'] = 0 row = row+1 display(df)
优化建议
1. 并行处理网络请求(最核心提速点)
代码慢的主要原因是串行等待网络响应:每只股票都要单独请求yfinance接口,大部分时间耗在等待上。改用线程池并行处理,一次性批量请求多只股票数据:
from concurrent.futures import ThreadPoolExecutor import yfinance as yf import pandas as pd from currency_converter import CurrencyConverter c = CurrencyConverter() def process_single_ticker(ticker_symbol): """单独处理一只股票,返回结果字典""" ticker = yf.Ticker(ticker_symbol) result = { 'Company': '', 'P/S': 0, 'P/E': 0, 'P/B': 0, 'Profit Margin(%)': 0, 'ROE(%)': 0, 'Current Ratio': 0 } # 获取基础财务数据 quarterly_financials = ticker.quarterly_financials quarterly_balance_sheet = ticker.quarterly_balance_sheet balance_sheet = ticker.balance_sheet if hasattr(ticker, 'balance_sheet') else None # 处理市值与货币转换 try: currency = ticker.info.get('currency', '').replace('GBp', 'GBP') financial_currency = ticker.info.get('financialCurrency', currency) market_cap = ticker.info.get('marketCap', 0) if currency != financial_currency and market_cap != 0: market_cap = c.convert(market_cap, currency, financial_currency) except: market_cap = 0 # 提取营收、净利润 total_revenue = quarterly_financials.loc['Total Revenue'].iloc[-1] if 'Total Revenue' in quarterly_financials.index else 0 net_income = quarterly_financials.loc['Net Income'].iloc[-1] if 'Net Income' in quarterly_financials.index else 0 # 提取流动资产、流动负债 total_current_assets = 0 total_current_liabilities = 0 if quarterly_balance_sheet is not None and 'Total Current Assets' in quarterly_balance_sheet.index: total_current_assets = quarterly_balance_sheet.loc['Total Current Assets'].iloc[-1] total_current_liabilities = quarterly_balance_sheet.loc['Total Current Liabilities'].iloc[-1] elif balance_sheet is not None and 'Total Current Assets' in balance_sheet.index: total_current_assets = balance_sheet.loc['Total Current Assets'].iloc[-1] total_current_liabilities = balance_sheet.loc['Total Current Liabilities'].iloc[-1] # 提取总资产、总负债 total_assets = 0 total_liabilities = 0 if quarterly_balance_sheet is not None and 'Total Assets' in quarterly_balance_sheet.index: total_assets = quarterly_balance_sheet.loc['Total Assets'].iloc[-1] total_liabilities = quarterly_balance_sheet.loc['Total Liab'].iloc[-1] elif balance_sheet is not None and 'Total Assets' in balance_sheet.index: total_assets = balance_sheet.loc['Total Assets'].iloc[-1] total_liabilities = balance_sheet.loc['Total Liab'].iloc[-1] # 计算各项指标 result['Company'] = ticker.info.get('longName', '') if total_revenue != 0: result['P/S'] = round(market_cap / total_revenue, 2) result['Profit Margin(%)'] = round((net_income / total_revenue)*100, 2) if net_income != 0: result['P/E'] = round(market_cap / net_income, 2) equity = total_assets - total_liabilities if equity != 0: result['P/B'] = round(market_cap / equity, 2) if net_income != 0: result['ROE(%)'] = round((net_income / equity)*100, 2) if total_current_liabilities != 0: result['Current Ratio'] = round(total_current_assets / total_current_liabilities, 2) return result # 批量并行处理 tickers = ['EDF.PA', 'anto.l', 'meta', 'iag.l', 'PFE','PKG','0700.hk', 'mbg.de'] with ThreadPoolExecutor(max_workers=10) as executor: results = list(executor.map(process_single_ticker, tickers)) # 生成最终DataFrame df = pd.DataFrame(results) display(df)
2. 精简冗余代码
- 删除无用导入:
matplotlib、numpy、datetime在原代码中无实际用途,直接移除减少初始化开销。 - 去掉原代码中
TTM相关的冗余计算(若无需保留该逻辑),或简化计算步骤。
3. 优化DataFrame操作
原代码用df.at[row, ...]逐行修改DataFrame,这种操作效率极低。改为先将所有结果收集到列表,最后一次性生成DataFrame,速度可提升数倍。
4. 简化异常处理
用dict.get()、条件判断替代嵌套try-except块,既降低代码复杂度,也减少不必要的异常捕获开销。比如获取ticker.info字段时,直接用get方法设置默认值,无需嵌套try。
5. 本地缓存数据(可选)
若需多次运行脚本,可将已获取的股票数据缓存到本地文件(如用pickle),下次运行直接读取缓存,避免重复请求网络。
内容的提问来源于stack exchange,提问作者Luca Charalambides
相关产品推荐
相关产品推荐

