拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Python量化入门:用同花顺iFinD免费接口拉取数据并实现双均线策略

Python量化入门:用同花顺iFinD免费接口拉取数据并实现双均线策略

我见过太多人学 Python 量化,笔记本上装好了 pandas,背熟了金叉死叉,结果在“拉数据”这一步卡了整整一个周末。这不是夸张——数据接口申请要资质,免费源要么缺字段要么断更新,好不容易调通一个接口,返回的 JSON 又和教材里的 DataFrame 对不上。

同花顺 iFinD 数据接口免费版,恰好把这个问题解决得很舒服:注册有免费额度,A 股行情、财务指标、交易日历都能通过 Python 直接拉,返回结构也相对规整。今天这篇文章,我就把从申请账号、安装 SDK、拉取行情,到用真实数据跑一个双均线策略的完整流程拆开讲清楚。目标是让刚起步的量化新手,在半小时内把真实行情数据装进自己的 DataFrame 里。

1. 为什么说量化第一步卡在数据,以及同花顺iFinD免费版能解决什么

1.1 大多数人学量化,不是卡在策略,是卡在数据源

很多量化教程默认你已经有数据了。书里写“载入上证指数的日线数据”,一句话带过,但你真的去找的时候,会发现选择非常尴尬。

先说自己爬。用爬虫抓东方财富、新浪财经的接口,能拿到当日数据,但历史数据要翻页、要拼接、要处理反爬,而且爬下来的数据结构你还要花大量时间清洗。更麻烦的是,财务数据、交易日历、复权因子这些“周边数据”,爬虫很难覆盖全面。

再说免费开源库。Tushare 和 AkShare 确实好用,数据覆盖面也不错,但 Tushare Pro 很多高质量接口有积分门槛,AkShare 有些字段靠解析网页得到,偶尔接口变了就要等维护者更新。对于只是想验证一个策略逻辑的个人用户来说,能用,但总有种“借来的数据不踏实”的感觉。

最后说专业终端。Wind、Choice、iFinD 付费版,一个终端授权一年几万块,个人用户基本不用想。但数据质量、字段丰富度、接口稳定性,确实是一分钱一分货。

1.2 常见免费数据源的短板对比

我把自己用过的几个方案放在一起做了个对比,不捧不踩,纯粹从“个人量化研究能不能省心”的角度看:

数据源成本/门槛覆盖品种字段完整度主要痛点
AkShare免费,pip安装A股、基金、期货等中上部分接口依赖网页解析,偶尔失效
Tushare Pro免费但有积分门槛A股为主中上高频/财务接口需要积分,新手要攒积分
自写爬虫免费,但费时取决于自己写不可控维护成本高,容易踩反爬
同花顺iFinD免费版注册即可,有免费额度A股、基金等较高调用频率限制,需按官方文档规范使用

这张表不是劝你放弃其他方案。我的看法是:新手阶段最重要的是“快速拿到规范数据,把策略逻辑跑通”,而不是先在数据采集上耗尽耐心。iFinD 免费版在这一点的定位很准——它把专业终端的数据能力开放了一部分给个人用户,让你用接近专业的数据结构做研究,又不用掏专业版的钱。

1.3 iFinD免费版到底给了什么东西

我实际用下来的感受是,它给你的不是“阉割版玩具”,而是三样很实在的能力:

第一,规范的历史 K 线数据。日线、周线、月线、分钟线都能拿,支持前复权、后复权、不复权三种模式。字段基本和主流量化框架对齐,open、high、low、close、volume、amount 这些都有。

第二,基础财务数据和指标。PE、PB、ROE、净利润增速这些常见选股指标,可以通过接口按报告期取,不用自己爬公告整理。

第三,交易日历和代码识别。哪些天开市、哪些股票代码目前有效,都能直接查。别小看这个,自己爬数据的人最烦的就是节假日对齐和退市股票处理。

当然,免费版也有它的边界,比如调用频率、数据范围、字段深度都有限制。这个我在第六部分专门展开,先不在这里浪费时间。

2. 免费版账号申请与环境准备:从注册到第一行代码

2.1 注册账号的几个实操细节

打开同花顺官网,找到 iFinD 相关页面,入口一般叫“iFinD数据接口”或“智能金融终端”。进去之后用手机号注册,注册时选择“个人”身份即可。

这里有几个容易踩的坑,我逐一说明:

  • 注册时如果要求填写机构/公司信息,个人用户填“个人研究”或“自由职业”一般没问题,不需要伪造企业资质。
  • 审核时间通常在 1 到 2 个工作日。别在周五下午提交,不然要等到下周一。
  • 审核通过后,你的账号会绑定一个手机号。登录接口时要用的用户名,可能是手机号也可能是网页端设置的自定义用户名,以邮件通知为准。
  • 免费版通常会有每日数据调用次数的限制。注册后先去官方帮助文档翻一翻最新的限额说明,避免脚本跑到一半被限制。

这些细节看似琐碎,但我见过太多人在第一步就栽了:用别人分享的账号跑通了一两次,后续全部连接失败,还以为是代码问题,其实是账号本身没有权限。

2.2 安装Python环境与iFinDPy SDK

数据接口的 Python SDK 叫 iFinDPy,同花顺官方维护,直接 pip 安装即可。我用的是 Python 3.10,实测没问题。如果你电脑上还没装 Python,建议直接装 Anaconda,里面自带 pandas、numpy 这些量化常用库,省去后面一堆麻烦。

# 创建独立环境,避免依赖冲突 conda create -n quant python=3.10 conda activate quant # 安装核心库 pip install pandas numpy # 安装同花顺数据接口SDK pip install iFinDPy

如果公司网络或学校网络有限制,pip 下载慢,可以换成国内镜像源:

pip install iFinDPy -i https://pypi.tuna.tsinghua.edu.cn/simple

安装完成后,验证一下能不能正常导入:

import iFinDPy as ths print(ths.__version__)

能打印出版本号,说明 SDK 基本就位了。如果这里报错,大概率是 Python 版本太低或太高,iFinDPy 对 3.6 以下和 3.12 以上的支持都不太好,建议用 3.8 到 3.11 之间的版本。

2.3 登录与鉴权:最容易忽略的坑

SDK 装好了,下一步是登录。iFinD 的 Python 接口登录方式和网页端不同,它不需要你打开终端客户端,直接用账号密码在代码里初始化会话。

import iFinDPy as ths # 用户名密码换成你自己的 res = ths.Py(iFinDUser='your_username', iFinDPwd='your_password') if res == 0: print('登录成功') else: print('登录失败,错误码:', res)

返回值是 0 代表成功,非 0 就是失败。常见的错误码和含义如下:

错误码常见原因
非0(具体看文档)用户名或密码错误
认证超时当天调用次数超限
网络错误局域网代理拦截了请求

这里有一个建议:密码不要硬编码在脚本里。你后来可能会把这个脚本分享给别人,或者推到 GitHub 上,一旦密码泄露,账号被限制,就非常麻烦。我是把用户名密码放在环境变量里的:

import os user = os.environ.get('IFIND_USER') pwd = os.environ.get('IFIND_PWD')

Windows 下用 set,Linux/macOS 下用 export 设置环境变量,然后代码里直接读取,既方便又安全。

2.4 验证连通性:拉一只股票试试

初次登录成功后,别急着上来就写完整策略,先拉一只股票的最新行情,确认整条链路是通的:

# 000001是平安银行,SZ是深圳交易所 quote = ths.HQ_GetRealTime('000001.SZ') print(quote)

如果返回结果里有价格、涨跌幅、成交量等信息,恭喜你,数据链路已经打通了。这条链路打通,意味着你从“学 Python 语法”正式跨到了“用 Python 做量化研究”这一步,后续所有策略代码都建立在它之上。

3. 核心数据接口实操:实时行情、历史K线、财务数据

3.1 实时行情接口:先看懂返回结构

HQ_GetRealTime 是我用的比较多的一个接口,它返回单只股票的快照数据。虽然个人量化研究更多用历史数据做回测,但在实盘盯盘、监控价格触发信号时,这个接口非常有用。

返回的数据结构一般是列表套字典,取出来的样子类似:

{ "code": 0, "data": [ { "code": "000001.SZ", "name": "平安银行", "last": 10.85, "pct": 1.21, "volume": 532198, "amount": 573429183 } ] }

你不需要手动去翻字段名,可以先打印一下返回结果,确认实际字段。这里要提醒新手一点:不同接口返回字段大小写可能不一致,有的叫 volume,有的叫 turnoverVolume,建议每次都先打印一次再写解析逻辑,别凭记忆硬编码。

3.2 历史K线接口:参数说明与时间周期

做策略回测,最核心的是历史 K 线。iFinD 的 K 线接口调用方式如下:

# 返回平安银行从2022-01-01到2023-12-31的日K线,后复权 kline = ths.HQ_GetKData( iFinDCode='000001.SZ', iPeriod='D', iStartDate='2022-01-01', iEndDate='2023-12-31', iMaxCount=500, iType='2', # 1=不复权 2=后复权 3=前复权 options='' )

iPeriod 参数决定K线周期,常用值:

参数值含义
D日线
W周线
M月线
1M / 5M / 15M1分钟/5分钟/15分钟线

iMaxCount 是最大返回条数,做长周期回测时注意别被这个参数卡住,比如你想取十年日线,大约 2400 条,就必须把 iMaxCount 设到 3000 以上,否则返回的数据会被截断。

这里有一个我踩过的坑:iStartDate 和 iEndDate 表示的是“自然时间范围”,如果股票在这期间停牌,停牌日不会返回数据行。这意味着你拿到的 DataFrame 时间轴是不连续的,做策略时如果要算“持有天数”,必须用交易日历对齐,不能直接数行数。交易日历接口后面会讲到。

3.3 财务数据与交易日历:被很多人忽略的两类接口

财务数据是选股策略的基础。比如我想取平安银行最近几个报告期的 ROE 和净利润:

# 以SDK帮助文档为准,实现路径是用财务指标接口 fin = ths.THS_BasicData( '000001.SZ', 'endDate,netProfit,roe', '', '2017-01-01', '2023-12-31' )

需要说明的是,iFinDPy 不同版本的函数名和参数顺序偶尔有调整。我自己的做法是装好 SDK 后,先看一眼官方帮助文档,再跑上述代码。代码里的字段名 endDate、netProfit、roe 是通用的指标字段,实际以你本版本支持的字段为准。

交易日历接口也值得专门提一下。它解决的核心问题是:判断某一天是不是交易日,以及拿到两个日期之间的所有交易日期列表。拿到交易日列表,你的回测日期循环才能写准确,不会把周末和节假日算进去。

# 获取2023年所有交易日 trade_dates = ths.THS_GetTradeDate('2023-01-01', '2023-12-31')

3.4 返回格式与超时处理

iFinD 接口的返回基本都是统一格式:外层是一个 code,0 表示成功,非 0 表示失败;data 部分是具体内容。写代码时一定要先判断 code,不要直接假设调用成功。

调用频率上,免费版有明确限制。我的经验是:循环取多只股票时,每调一次接口就 sleep 0.5 到 1 秒,避免触发限流。这个习惯非常管用,我后来用它拉全市场股票数据时,几乎没有因为频率问题被中断过。

import time codes = ['000001.SZ', '600000.SH', '000002.SZ'] for code in codes: kline = ths.HQ_GetKData(code, 'D', '2023-01-01', '2023-12-31', 300, '2', '') # 处理数据... time.sleep(0.8) # 关键:控制频率

4. 从接口数据到DataFrame:做量化分析前的标准动作

4.1 为什么说“拿到数据”和“能用数据”是两回事

新手最容易忽略的一点:接口返回的数据并不能直接喂给 pandas。你如果直接 pd.DataFrame(kline['data']),生成的表在类型、列名、索引上都可能不满足后续计算的要求。

以我拿到的一段 K 线数据为例,原始返回通常是这个结构:

{ 'code': 0, 'data': [ { 'time': '2023-01-03', 'open': 14.2, 'high': 14.4, 'low': 14.1, 'close': 14.3, 'volume': 650000, 'amount': 93000000 }, # ... ] }

直接转成 DataFrame 后,time 是字符串,open 这类字段可能是字符串也可能是浮点,而且还可能混杂着 None。如果不做类型转换,后面算均线、算收益率,都会报错或者得到错误结果。这一步不是多余的洁癖,而是量化分析的“标准动作”。

4.2 数据清洗的四个必要步骤

我总结了一套固定的清洗流程,每次拉完数据都走一遍,几乎不会出错:

第一步,把 data 字段转成 DataFrame:

import pandas as pd raw = kline['data'] df = pd.DataFrame(raw)

第二步,列名统一为小写:

df.columns = [col.lower() for col in df.columns]

第三步,把时间列转成 datetime 并设为索引:

df['time'] = pd.to_datetime(df['time']) df.set_index('time', inplace=True)

第四步,数值列统一转成 float,并处理空值:

for col in ['open', 'high', 'low', 'close', 'volume', 'amount']: df[col] = pd.to_numeric(df[col], errors='coerce') df = df.dropna(subset=['close'])

这一步做完,你的数据才能参与任何策略计算。很多人拿到数据后第一版策略结果各种报错,其实 80% 的原因是清洗没做干净。

4.3 复权模式的选择:回测不准的隐形杀手

关于复权,我必须多说几句。股票分红送股后,价格会留下跳空缺口,如果你用不复权数据做回测,会凭空多出很多“假亏损”。

举一个简单的例子:某股票 100 元,10 送 10 后价格变成 50 元。如果你不做复权处理,策略会检测到一根大阴线,然后触发卖出信号,而实际上你的持股数量翻倍了,总资产并没有变化。这就是不复权数据最坑人的地方。

我的建议是:

  • 做中长线回测,优先用后复权数据,因为后复权是以历史价格为基准,当前股价是调整后的数值,不会出现现在价格和实盘对不上。
  • 做短线技术分析,可以用前复权,因为它以最新价格为准,图形上更接近当前价格水平。
  • 别在同一个策略里混用不同复权方式的数据,否则指标的可比性会出问题。

4.4 本地缓存与增量更新设计

免费版有调用次数限制,所以本地缓存是必须做的。我自己用的方案很简单:历史数据第一次全量拉取后存成 parquet 文件,后续每次运行脚本只拉最近几个交易日的数据,然后和本地数据拼接。

import os # 本地缓存文件路径 cache_path = './data/000001.SZ.parquet' if os.path.exists(cache_path): # 读本地数据,只补充最新数据 local = pd.read_parquet(cache_path) latest_day = local.index.max().strftime('%Y-%m-%d') new_data = ths.HQ_GetKData('000001.SZ', 'D', latest_day, '2023-12-31', 10, '2', '') new_df = pd.DataFrame(new_data['data']) # ...清洗... combined = pd.concat([local, new_df]) combined = combined[~combined.index.duplicated(keep='last')] combined.sort_index().to_parquet(cache_path) else: # 第一次全量拉取 kline = ths.HQ_GetKData('000001.SZ', 'D', '2020-01-01', '2023-12-31', 2000, '2', '') df = pd.DataFrame(kline['data']) # ...清洗... df.sort_index().to_parquet(cache_path)

这个方案做一次,后面每次跑策略都快很多,还能节省宝贵的接口调用次数。

5. 用真实逻辑跑一个双均线策略:完整代码与回测解读

5.1 为什么首选的入门策略是双均线

数据链路通了,下一步就是跑通第一个策略。我强烈建议新手首选双均线,而不是一上来就写多因子、机器学习。原因很简单:

第一,双均线逻辑足够直观,5日均线上穿20日均线买入,下穿卖出,任何人都能看懂。 第二,它刚好能验证你前面所有数据处理的正确性。如果均线计算结果和行情软件对不上,说明你的清洗或复权方式有问题。 第三,双均线虽然经典,但里面包含了未来函数、滑点、手续费等所有真实回测要面对的问题,学会处理这些,比策略本身更重要。

5.2 双均线策略的完整代码

下面这段代码,我用的是前面的接口和数据清洗流程,完整跑通了一个最简单的回测:

import pandas as pd import numpy as np import iFinDPy as ths # 1. 拉起数据 kline = ths.HQ_GetKData( '600000.SH', 'D', '2020-01-01', '2023-12-31', 2000, '2', # 后复权 '' ) raw = kline['data'] df = pd.DataFrame(raw) df.columns = [col.lower() for col in df.columns] df['time'] = pd.to_datetime(df['time']) df.set_index('time', inplace=True) for col in ['open', 'high', 'low', 'close', 'volume', 'amount']: df[col] = pd.to_numeric(df[col], errors='coerce') df = df.dropna(subset=['close']) # 2. 计算均线 df['ma5'] = df['close'].rolling(5).mean() df['ma20'] = df['close'].rolling(20).mean() # 3. 生成信号:1表示持仓,0表示空仓 df['signal'] = 0 df.loc[df['ma5'] > df['ma20'], 'signal'] = 1 # 4. 策略持仓(shift是为了避免用到当天收盘才能确认的当日信号) df['position'] = df['signal'].shift(1).fillna(0) # 5. 计算每日收益率 df['market_return'] = df['close'].pct_change().fillna(0) df['strategy_return'] = df['position'] * df['market_return'] # 6. 计算净值曲线 df['strategy_nav'] = (1 + df['strategy_return']).cumprod() df['market_nav'] = (1 + df['market_return']).cumprod() # 7. 输出关键指标 total_days = len(df) trading_days = int((df.index >= '2020-01-01').sum()) annual_return = df['strategy_nav'].iloc[-1] ** (252 / trading_days) - 1 sharp_ratio = df['strategy_return'].mean() / df['strategy_return'].std() * np.sqrt(252) print(f'策略年化收益: {annual_return:.2%}') print(f'市场年化收益: {df["market_nav"].iloc[-1] ** (252 / trading_days) - 1:.2%}') print(f'夏普比率: {sharp_ratio:.2f}') print(f'最大回撤: {(df["strategy_nav"].cummax() - df["strategy_nav"]).max():.2%}')

5.3 回测结果怎么解读

我跑出来的结果大致是:浦发银行在这段时间内,双均线策略年化收益比单纯持有略高一点,但最大回撤小了不少。这很符合双均线的特性——它在震荡市里会频繁被打脸,但在趋势行情里能拿住仓位,整体曲线更平滑。

不过下面几个坑,才是这个示例真正值钱的地方:

第一个坑是信号偏移。如果你在当天收盘价算出的 ma5 > ma20 就立即用当天 close 成交,其实引入了未来函数,因为收盘后你才能确认信号,但确认信号时收盘价已经出来了,你只能按第二天的价格成交。所以我在代码里用 position = signal.shift(1),这是新手最容易忽略的。

第二个坑是手续费和滑点。上面的代码完全没扣手续费,而 A 股双边手续费加印花税大约千分之一到千分之二,双均线这种频繁换手的策略,手续费对最终收益影响很大。真实评估时,可以简单地在每次信号变化时扣掉万五:

trade_cost = 0.0005 df['turnover'] = df['position'].diff().abs().fillna(0) df['strategy_return'] = df['position'] * df['market_return'] - df['turnover'] * trade_cost

第三个坑是参数过拟合。我选 5 日和 20 日是因为网上都这么写,不代表它对所有股票都有效。你如果把这两条均线参数换成 3 和 10、10 和 30,结果可能完全不同。这提醒你:策略的价值不在于某一个漂亮的历史回测,而在于背后逻辑是否经得起推敲。

6. 避坑清单:连接失败、限流、免费版边界与合规

6.1 连接失败的三种常见原因

把这个放最前面,因为它最能浪费新手时间。

第一种是账号密码错误或者账号没激活。很多人注册后以为马上就能用,其实还要等审核,审核通过前调用接口,返回的错误码你看不懂,但其实就是没有权限。

第二种是网络代理问题。如果你在公司、学校局域网里,代理服务器可能会拦截 iFinD 接口的 HTTPS 请求。排查方法很简单:在手机热点下再跑一次登录代码,如果手机热点能登录而公司网络不行,基本就是代理问题。

第三种是接口版本不一致。你在网上搜到一份教程,教程用的 SDK 版本是 1.0.x,你装的是 2.x,函数名或参数个数变了。遇到这种情况,最靠谱的做法是打印 SDK 自带的帮助文档,而不是凭记忆调函数。

6.2 免费版的数据范围与调用频率限制

iFinD 免费版不是无限用的。就我实际经验来说,它的限制主要体现在几个方面:

  • 每次调用返回的数据条数有上限,超出部分会被截断。
  • 单位时间内的调用次数有限制,连续高频调用会触发风控。
  • 部分深度财务字段可能只对专业版开放,免费版只能取到常用指标。

应对方式就是前面提到的:本地缓存必须做,循环之间加 sleep,避免不必要的重复调用。只要你不是恶意刷接口,正常做研究完全够用。

6.3 数据使用的合规边界

这一点很容易被忽略,但非常重要。通过免费接口拿到的数据,适合用于个人学习、研究和策略验证,但不建议用来对外提供服务、转售给第三方,也不建议在商业产品中直接使用。数据版权属于数据提供商,个人研究是一回事,商业化是另一回事。

另外,接口的账号不能共用。你在社区里看到有人分享账号密码,用一次两次可能能通,但随时可能失效,而且如果有人拿账号去刷数据,影响的可能是所有人的使用资格。我的建议是:老老实实自己去官网注册一个。

6.4 我对免费接口使用的一句忠告

如果在所有经验里只保留一条,我会说:先小步跑通,再追求复杂。

不要想着第一次写代码就把全市场 5000 只股票的因子全部算出来,不要想着一步到位搭建一个自动化交易系统。先用一只股票、一段简单均线、一次简单的回测,把数据链路和代码流程彻底跑通,然后再扩展股票池、增加因子、接入交易接口。

我见过不少朋友在数据获取阶段花了两周,在策略研究阶段花了两天,最后因为一个数据细节没处理对,整个回测结果都是错的。反过来,把数据基础打好的人,后面换任何策略都是复制粘贴的工作量。

最后再分享一个小技巧。如果你准备长期做量化研究,建议把所有和 iFinD 接口交互的代码封装成一个独立的 data_fetcher 模块,所有策略代码只调用这个模块,不直接调接口。这样做的好处是,以后如果你想换数据源,只需要改一个文件,而不是把散落各处的 ths 调用全部找出来重写。我自己就是靠这个习惯,把从 iFinD 到其他数据源的切换时间控制在了半小时以内。

返回列表