如何用Python获取全部股票的周线历史信息并自动更新

企业微信

为什么周线数据比日线更适合普通人

日线数据噪音太大。一根阳线改三观,两根阴线毁信仰。周线把五个交易日的波动压缩成一根K线,过滤掉盘中情绪化的假突破。对上班族来说,每周只盯一次收盘价,交易频率降下来,犯错概率也跟着降。

做期货同理。大宗商品的周线级别趋势往往对应产业周期的拐点,日线里的随机跳动在周线上会被平滑掉。用周线做趋势跟踪,滑点和手续费对账户的侵蚀小得多。

如何用Python获取全部股票的周线历史信息并自动更新

问题来了:从哪里搞到全部股票的周线数据?手动下载不现实,五千多只股票挨个导出,光点击鼠标就能把手指磨出茧子。Python批量抓取是唯一靠谱的路子。

两条主流路线:tushare和akshare怎么选

抓A股数据绕不开这两个库。tushare是老牌选手,接口稳定,字段规范,周线数据直接有现成的weekly接口。缺点是积分门槛:注册用户只能调基础接口,想要全市场周线,积分得攒到一定级别。对愿意花点小钱或者花时间做任务的人来说,这不是大问题。

akshare是后起之秀,完全免费,数据源来自东方财富、新浪财经等公开页面。它没有积分体系,调取全部股票周线的接口是stock_zh_a_hist,把周期参数设为weekly就行。代价是接口偶尔会因为源站改版而失效,需要关注库的更新版本。

两条路都走通的人不少。稳妥做法是tushare做主数据源,akshare做备用校验。期货数据方面,akshare的futures_zh_weekly能抓国内商品期货周线,tushare的fut_weekly也有对应接口,但期货品种少,几十个主力合约,手动维护成本低得多。

实操第一步:拿到全部股票代码列表

不管用哪个库,第一步都是搞到股票池。tushare的stock_basic接口返回所有上市股票的基本信息,包括代码、名称、上市状态。过滤掉退市和暂停上市的,剩下就是活跃标的。

akshare对应的是stock_info_a_code_name,返回代码和名称的表格。这个接口不区分上市状态,需要自己用行情接口再筛一遍。

代码长这样:


import akshare as ak

stock_list = ak.stock_info_a_code_name()

codes = stock_list['code'].tolist()

print(len(codes))

五千多个代码,打印出来能刷屏。存到本地CSV或者数据库里,后面更新时只拉新增的代码,避免重复劳动。

批量拉周线的核心循环与限速

拿到代码列表后,写一个for循环逐个调接口。这里有个坑:akshare的数据源对高频请求敏感,连续快速调用会被封IP。必须加延时。


import time

import pandas as pd

all_weekly = []

for code in codes:

    try:

        df = ak.stock_zh_a_hist(symbol=code, period='weekly', start_date='20100101', adjust='qfq')

        df['code'] = code

        all_weekly.append(df)

    except Exception as e:

        print(f'{code} failed: {e}')

    time.sleep(0.3)

result = pd.concat(all_weekly, ignore_index=True)

result.to_parquet('all_stock_weekly.parquet')

延时0.3秒是经验值。五千只股票跑完大概需要二十五分钟到半小时。嫌慢可以开多线程,但线程数别超过3,再高容易触发源站风控。期货数据量小,不需要限速。

存成parquet格式比CSV快十倍,文件体积小一半。后续读取用pandas直接加载,内存占用可控。

周线重采样:自己算比接口更可靠

有些接口的周线数据是按自然周划分的,遇到节假日会出现空周或者错位。更稳的做法是拉日线数据,自己用pandas的resample函数合成周线。


df = ak.stock_zh_a_hist(symbol='000001', period='daily', start_date='20200101', adjust='qfq')

df['日期'] = pd.to_datetime(df['日期'])

df.set_index('日期', inplace=True)

weekly = df.resample('W-FRI').agg({

    '开盘': 'first',

    '最高': 'max',

    '最低': 'min',

    '收盘': 'last',

    '成交量': 'sum'

}).dropna()

W-FRI表示以周五为周末锚点,遇到节假日自动取当周最后一个交易日。这样合成的周线和交易所公布的数据完全一致,还避免了接口字段缺失的问题。

日线转周线的代价是数据量大。五千只股票十年的日线,下载时间翻好几倍。适合对数据质量要求高的场景,比如量化回测。只是看个趋势的话,直接用接口的周线字段省事。

复权处理决定回测成败

股票周线数据必须做复权。不复权的价格在除权除息日会出现巨大跳空,均线全部失真。前复权以当前价格为基准调整历史价格,适合看盘和回测。后复权以上市价格为基准,适合计算长期收益率。

akshare的adjust参数填qfq是前复权,hfq是后复权,空字符串是不复权。tushare的周线接口默认不复权,需要额外调复权因子接口换算。

期货没有复权概念,但主力合约换月会产生价格跳空。处理办法是拼接主力连续合约,或者用指数合约代替。akshare的futures_main_sina返回的是主力连续合约,已经做了换月平滑处理。

增量更新比全量抓取重要一百倍

第一次全量拉取花半小时可以接受。每天收盘后再全量拉一遍就是灾难。正确做法是增量更新:本地存一个最后交易日字段,每次只拉这个日期之后的新数据。


last_date = pd.read_parquet('all_stock_weekly.parquet')['日期'].max()

new_data = []

for code in codes:

    df = ak.stock_zh_a_hist(symbol=code, period='weekly', start_date=last_date.strftime('%Y%m%d'), adjust='qfq')

    if len(df) > 0:

        df['code'] = code

        new_data.append(df)

    time.sleep(0.3)

if new_data:

    old = pd.read_parquet('all_stock_weekly.parquet')

    merged = pd.concat([old, pd.concat(new_data)], ignore_index=True)

    merged.drop_duplicates(subset=['code', '日期'], keep='last', inplace=True)

    merged.to_parquet('all_stock_weekly.parquet')

增量更新跑一次只要一两分钟。周线数据每周才更新一次,周五收盘后跑一遍就够。

数据落地之后能干什么

全部股票的周线数据躺在本地硬盘上,能做很多事。筛出周线MACD金叉的股票,算全市场周线级别的动量因子,做行业轮动的周频回测。期货那边用周线数据画跨品种的比价曲线,找产业链利润错配的机会。

数据是原材料,加工方式决定产出。有人拿周线数据做选股,有人做仓位管理。核心原则只有一条:别用未来数据。周线合成时,周五收盘前最后一根K线还没走完,回测里不能拿这根未完成的K线做决策。把resample的结果整体后移一根,或者只在周五收盘后更新数据,能避开这个坑。

抓数据这件事,工具越简单越好。花里胡哨的分布式爬虫框架,对五千只股票的周线数据来说是杀鸡用牛刀。一个for循环加0.3秒延时,配上增量更新逻辑,足够跑十年。

转载请注明出处:https://www.lianghuajiaoyi.top/wenzhang/Python-gupiao-zhouxian-1401.html