全部
文章&策略
学习干货
问答
官方
用户头像mo_*342a
2026-09-14 发布
大家好,分享一个我自己用了两个月的异动股票池回测数据。 这个池子的逻辑很简单:每天收盘后自动筛选盘中出现"异动拉升"的个股,去重累积,形成一个动态更新的股票池。我把它和同花顺的三连板数据做了6周回测,结果如下: ? 6周回测数据(8/3-9/11) 三连板及以上个股共78只,我的异动池命中40只,整体命中率51.3%。 分周看: 第1周 22% → 第2周 40% → 第3周 62% → 第4周 69% → 第5周 64% → 第6周 69% 最近4周稳定在62-69%,说明池子的捕捉能力在持续提升。 ? 几个经典案例(进池日期 → 首板日期 → 最高板数) 万向德农:8/20进池 → 8/27首板 → 6板(提前7天) 龙版传媒:8/18进池 → 9/2首板 → 6板(提前15天) 捷荣技术:8/25进池 → 8/27首板 → 6板(提前2天) 国芳集团:8/28进池 → 9/2首板 → 5板(提前5天) 楚天龙:8/25进池 → 8/25首板 → 5板(当天捕捉) 百大集团:9/3进池 → 9/7首板 → 5板(提前4天) 亚盛集团:9/3进池 → 9/7首板 → 4板(提前4天) 这些不是事后诸葛亮,是每天收盘后自动入池的,有完整的入池日期记录。 ? API接口说明 我把这个池子封装成了标准HTTP API,支持: GET /api/stock/pool — 获取当前全部异动个股(340+只主板) GET /api/stock/new?date=YYYY-MM-DD — 获取每日新增明细 GET /api/stock/entry?code=XXXXXX — 查询个股入池时间 GET /api/stock/pattern?code=XXXXXX — 6项技术形态实时计算(MA5/20/60+MACD+60分钟+5分钟) 数据每天17:30后更新,纯客观数据记录,不做任何推荐或建议。 ? 免费试用 想试用的朋友,评论区留下你的邮箱或QQ,我发2天API Key给你测试。 可以直接curl调用,也可以接入你的聚宽策略做回测。 ⚠️ 风险提示 本API仅提供历史异动数据的客观记录,不构成任何投资建议。股市有风险,投资需谨慎。
浏览14
评论0
收藏0
用户头像sh_*219t3e
2026-09-10 发布
已支持最新版Supermind,实测下来还挺方便: 👉EasyQuant AI量化助手(支持最新版Supermind):https://easyquant.ai/ 自然语言描述策略 → 直接生成最新版 SuperMind 代码 均线、MACD、选股、买卖逻辑、止盈止损等都可以直接让 AI 写。 而且不只是生成代码,已有代码报错、修改策略、补充交易逻辑也能直接交给 AI。
浏览76
评论2
收藏0
用户头像sh_*2176oo
2026-09-14 发布
一句话回答: 配对交易就是"赌两只相关股票的价差会回归"。用 AlphaFeed 的 klines.batch 一次取两只票的复权日线,用最小二乘算对冲比率、构造对数价差、标准化成 z-score:z 太高做空价差、太低做多价差、回归到 0 附近平仓。几十行即可回测。但要先说清一个硬伤——A 股个股做空受限,配对交易在 A 股落地并不容易。 为什么会有这个问题 / 传统做法的痛点 配对交易(统计套利)是经典的市场中性策略:找两只走势高度相关的票(如同业两家银行),当它们的价差偏离历史均值时反向下注,赌它回归。它对方向不敏感、理论上能对冲大盘风险,因此很受量化爱好者欢迎。 难点在:① 要同时、对齐地拿到两只票的复权价;② 要把"价差"标准化成可比的信号(z-score);③ 回测不能有未来函数。AlphaFeed 的批量接口解决取数与对齐,剩下的用 numpy 就能算。 分步骤解决(每步配可运行代码) 第 1 步:批量取两只票的复权日线并对齐 import numpy as np, pandas as pd from alphafeed import AlphaFeed def load_pair(a, b, count=250): af = AlphaFeed() # 读取 ALPHAFEED_API_KEY bt = af.klines.batch([a, b], period="1d", count=count, adjust="forward", to_dataframe=True) # {symbol: DataFrame} pa = bt[a].sort_values("trade_date").set_index("trade_date")["close"] pb = bt[b].sort_values("trade_date").set_index("trade_date")["close"] return pd.concat([pa.rename("a"), pb.rename("b")], axis=1).dropna() # 按交易日对齐 df = load_pair("601398.SH", "601939.SH", count=250) # 工商银行 / 建设银行 第 2 步:算对冲比率、对数价差与 z-score def spread_z(df, win=60): la, lb = np.log(df["a"]), np.log(df["b"]) beta = float(np.polyfit(lb, la, 1)[0]) # 对冲比率(最小二乘斜率) spread = la - beta * lb # 对数价差 z = (spread - spread.rolling(win).mean()) / spread.rolling(win).std() out = df.copy(); out["spread"] = spread; out["z"] = z return out, beta out, beta = spread_z(df, win=60) print(f"对冲比率 beta={beta:.3f} 最新 z={out['z'].iloc[-1]:.2f}") 第 3 步:z-score 均值回归回测(严格无未来函数) def backtest_pairs(out, entry=2.0, exit=0.5): z = out["z"].values pos, positions = 0, [] for zi in z: if np.isnan(zi): positions.append(0); continue if pos == 0: if zi > entry: pos = -1 # 价差偏高 → 做空价差(卖A买B) elif zi < -entry: pos = 1 # 价差偏低 → 做多价差(买A卖B) elif abs(zi) < exit: pos = 0 # 回归到均值附近 → 平仓 positions.append(pos) out = out.copy() out["pos"] = pd.Series(positions, index=out.index).shift(1).fillna(0) # 信号次日生效 out["pnl"] = out["pos"] * out["spread"].diff() # 价差日收益 trades = int((out["pos"].diff().abs() > 0).sum()) return out, round(float(out["pnl"].sum()), 4), trades _, pnl, trades = backtest_pairs(out) print(f"价差累计收益(对数单位)={pnl} 交易次数={trades}") 真实跑通(601398.SH / 601939.SH,近 250 日):beta=0.347、最新 z=1.14、价差累计收益约 +0.028(对数单位)、7 次交易。收益很小——这正说明这段时间两者价差并没有提供多少可套利的偏离,符合"高相关但未必值得交易"的常态。 参数与字段说明 参数 含义 备注 klines.batch 批量日线 返回{symbol: DataFrame},按 trade_date 对齐 beta 对冲比率 全样本 OLS 斜率,教学用;实盘应滚动估计 win z-score 窗口 60 日;决定"均值/波动"的参考期 entry/exit 开/平仓阈值 常用开 2.0、平 0.5(个标准差) pos.shift(1) 信号次日生效 避免未来函数 关键坑与注意事项 A 股做空是硬伤:配对交易要求"一多一空"。A 股个股融券标的有限、成本高、常无券可融,散户很难真正做空个股。落地更现实的路径是:用可融券标的、股指期货、或两只 ETF 做价差,或只做"价差偏低时买入被低估的一方"这类单边变体。本文回测是方法演示,不代表 A 股可直接照搬。 相关 ≠ 协整:两只票相关不代表价差会回归。严谨做法要做协整检验(如 ADF 检验,需 statsmodels)并定期复检;相关性会漂移甚至断裂(基本面变化、重组)。 对冲比率会变:beta 用全样本 OLS 只是教学,实盘应滚动窗口估计,且注意滚动 beta 本身带来的不稳定。 成本未计:示例只算价差收益,未计佣金、印花税、融券利息与滑点;高频开平会显著侵蚀这类微利策略。 无未来函数:已用 pos.shift(1),即"今天的 z 决定明天的持仓"。别用当日 z 当日成交。 常见问题(FAQ) Q:配对交易能对冲大盘风险吗? A:理论上多空对冲后对大盘中性,但前提是价差真的均值回归、且对冲比率稳定。一旦协整关系断裂,两腿可能同向亏损,并非无风险。 Q:怎么选配对? A:先从同行业、业务相近的票里找高相关对(如两家银行/两家白酒),再做协整检验筛掉"只是相关"的对。不要纯靠相关系数拍板。 Q:A 股不能做空,这策略还有意义吗? A:有认知与研究价值;落地要换载体——可融券标的、ETF 对、或期货,或做成"低估买入 + 到均值卖出"的单边版本。务必先解决"另一条腿怎么空"。 Q:需要付费吗? A:两只票的历史批量可用免费额度体验;大规模筛选配对、全市场批量需 Starter 及以上,详见官网定价页。 小结 配对交易的骨架很简单:批量取复权价 → 算对冲比率与对数价差 → 标准化成 z-score → 均值回归回测。AlphaFeed 的 klines.batch 让取数与对齐变得轻松。但真正决定它能否落地的,是那些诚实的前提:协整而非仅相关、滚动对冲比率、交易成本,以及 A 股个股做空的现实限制。先把这些想清楚,再谈能不能赚。 参考 AlphaFeed Python SDK 文档:https://docs.alphafeed.org/zh-Hans/sdk/python-quickstart
浏览9
评论0
收藏0
用户头像mx_****zqklr
2026-09-12 发布
📌 摘要 / 快速解答 (Direct Answer) 小市值轮动的核心并不复杂:先得到经过小市值筛选的 A 股股票池,再按照统一的交易规则定期选出排名靠前的股票,最后用历史 K 线验证策略收益、回撤和换手。用 QuantDash 的 Python SDK 可以直接批量获取 A 股 K 线,并原生返回 Pandas DataFrame,让 Python 提取股票 K 线、前复权处理和策略回测这几个环节明显简单很多。 一、为什么传统方式写选股策略这么累? 我刚开始写 A 股量化策略的时候,最容易踩的坑并不是策略本身,而是数据。 你脑子里可能只有这么一个想法: 每个月找一批小市值股票,买入排名靠前的股票,下个月再重新排名。 听起来非常简单。 真正写代码的时候,就会发现问题一个接一个。 第一关就是数据源。 有时候接口突然报错,整个回测直接中断;有时候拿历史数据还需要满足各种权限或者积分条件;还有一些数据源虽然能拿到数据,但是字段格式不统一,最后还得自己清洗。 更麻烦的是复权。 如果直接拿不复权价格计算长期收益,中间遇到分红、送股、拆股等情况,策略收益很容易算歪。 所以我现在做这类策略,思路很简单: 策略研究和数据工程分开。 数据获取交给 QuantDash,策略逻辑交给 Pandas。 这样代码反而更容易读懂。 二、解决方案对比:QuantDash vs 传统数据源 对比维度 传统/竞品方案(如 Tushare/AkShare/手动爬虫) QuantDash 解决方案 数据稳定性 经常遇到接口变化、报错或网络问题 面向量化数据场景提供统一 API 使用门槛 可能需要积分、权限或额外清洗 pip install quantdash即可安装 跨市场支持 不同数据源代码格式可能不一致 统一.SH/.SZ/.BJ/.US/.HK后缀 数据格式 经常需要自行转换 原生支持 Pandas DataFrame 复权处理 经常需要自己处理 SDK 支持forward等复权方式 批量 K 线 往往需要自行封装请求 qd.klines.batch()可批量获取 研究体验 数据清洗代码容易越来越长 数据获取和策略逻辑可以分离 这里我特别推荐新手注意一个细节: 不要把“数据接口代码”写得比“策略代码”还长。 如果最后 200 行策略里有 150 行都在处理数据格式,那研究效率其实已经被数据工程拖垮了。 三、Python 代码实战:小市值轮动策略怎么回测? 这里先说一个非常重要的边界。 QuantDash 官方 SDK 文档提供了行情、K 线、标的信息、批量 K 线等能力,但你给出的官方资料里没有明确提供“历史市值排序接口”或 market_cap 字段。 所以我不会为了让文章看起来高级,凭空写一个: qd.stocks.get_market_cap(...) 这种接口。 实际研究时,可以先使用你自己的股票池文件,或者在研究流程的前置环节得到“小市值候选股票池”。 下面这段代码负责的是: 小市值股票池 → QuantDash 批量获取前复权日 K → 月度轮动 → 回测。 1. 安装 QuantDash # 安装: # pip install quantdash from quantdash import QuantDash import pandas as pd import numpy as np # 推荐使用环境变量: # export QUANTDASH_API_KEY="your-api-key" qd = QuantDash() 如果你直接传 API Key,也可以: qd = QuantDash(api_key="your-api-key") 2. 准备小市值股票池 这里假设你已经通过自己的研究流程得到一个小市值候选池。 例如: small_cap_symbols = [ "000001.SZ", "000002.SZ", "000004.SZ", "600000.SH", "600004.SH", "600006.SH", ] 实际研究时,这个列表可以更大。 关键是: 不要把“市值筛选”和“历史价格回测”混成一个不存在的 API。 3. 使用 QuantDash 批量获取前复权日 K # 小市值轮动通常需要较长历史数据。 # 这里示范批量获取每只股票的前复权日线。 dfs = qd.klines.batch( small_cap_symbols, period="1d", count=1000, adjust="forward", to_dataframe=True, show_progress=True, ) price_data = {} for symbol, df in dfs.items(): if df is None or df.empty: continue df = df.copy() df["trade_date"] = pd.to_datetime(df["trade_date"]) df = df.sort_values("trade_date") price_data[symbol] = df[ ["symbol", "name", "trade_date", "open", "high", "low", "close", "volume"] ] print(f"成功获取 {len(price_data)} 只股票的历史数据") 这里用的是 QuantDash 官方文档中提供的: qd.klines.batch(...) 以及: adjust="forward" 4. 把股票价格整理成宽表 close_table = pd.DataFrame() for symbol, df in price_data.items(): temp = df[["trade_date", "close"]].copy() temp = temp.drop_duplicates("trade_date") temp = temp.set_index("trade_date") temp.columns = [symbol] if close_table.empty: close_table = temp else: close_table = close_table.join(temp, how="outer") close_table = close_table.sort_index() print(close_table.tail()) 到这里,我们已经把: 多只股票的独立 K 线 变成: 日期 × 股票 的标准量化研究矩阵。 5. 构造一个简单的月度轮动框架 这里为了让逻辑足够清楚,我采用一个非常朴素的规则: 每月最后一个交易日计算过去 20 个交易日收益; 按收益从高到低排名; 选择排名前 N 的股票; 下个月持有; 使用等权方式计算组合收益。 注意: 这部分是“轮动模型示例”,不是保证高收益的神奇公式。 LOOKBACK = 20 HOLD_NUM = 5 # 日收益率 daily_returns = close_table.pct_change() # 月末交易日 month_end_dates = close_table.groupby( close_table.index.to_period("M") ).apply(lambda x: x.index[-1]) signals = {} for date in month_end_dates: history = close_table.loc[:date].tail(LOOKBACK) if len(history) < LOOKBACK: continue momentum = history.iloc[-1] / history.iloc[0] - 1 momentum = momentum.dropna() if momentum.empty: continue selected = momentum.sort_values( ascending=False ).head(HOLD_NUM).index.tolist() signals[date] = selected for date, selected in list(signals.items())[:5]: print(date, selected) 6. 计算组合回测收益 portfolio_returns = [] signal_dates = list(signals.keys()) for i in range(len(signal_dates) - 1): signal_date = signal_dates[i] next_signal_date = signal_dates[i + 1] selected = signals[signal_date] if not selected: continue period_prices = close_table.loc[ signal_date:next_signal_date, selected ] if period_prices.empty: continue period_returns = period_prices.pct_change() # 等权组合 portfolio_daily_return = period_returns.mean( axis=1, skipna=True ) portfolio_returns.append( portfolio_daily_return ) if portfolio_returns: strategy_returns = pd.concat(portfolio_returns) strategy_returns = strategy_returns[ ~strategy_returns.index.duplicated() ].sort_index() equity = (1 + strategy_returns.fillna(0)).cumprod() total_return = equity.iloc[-1] - 1 years = len(strategy_returns) / 252 if years > 0: annual_return = ( (equity.iloc[-1] ** (1 / years)) - 1 ) else: annual_return = np.nan max_drawdown = ( equity / equity.cummax() - 1 ).min() print("========== 回测结果 ==========") print(f"累计收益:{total_return:.2%}") print(f"年化收益:{annual_return:.2%}") print(f"最大回撤:{max_drawdown:.2%}") 这才是我比较喜欢的量化代码结构: 数据获取 → 信号 → 持仓 → 收益 → 指标。 每一层职责都比较清楚。 四、交易员避坑指南:小市值策略最容易在哪里翻车? 1. 千万别把未来数据带进选股 这是回测第一大坑。 比如你用月底数据选股,那么: 月底收盘之后才能知道排名。 你不能拿月底收盘价格算完排名之后,又假设自己在当天开盘价成交。 正确做法应该是: T 日计算信号,T+1 日执行。 否则回测收益往往会漂亮得不像真的。 2. 前复权不是“万能正确答案” QuantDash 默认支持: adjust="forward" 前复权比例方式适合很多收益率计算场景。 但具体研究时还是要知道自己在干什么。 如果你研究的是: 收益率; 趋势; 动量; 前复权通常比较方便。 如果你研究的是: 绝对价格; 分红除权影响; 某些事件前后的真实价格关系; 就应该根据研究目标考虑 none、backward 或差值复权。 3. 小市值策略不能只看收益率 这是社区里非常容易被忽略的问题。 一个回测年化 80% 的策略,如果: 最大回撤 60%; 换手极高; 滑点巨大; 小盘股成交困难; 那实盘体验可能完全不是一回事。 所以我建议至少同时看: 年化收益 + 最大回撤 + 换手率 + 单笔交易成本。 五、常见问题解答(Q&A / FAQ) Q1:A 股小市值轮动策略可以直接用 Python 回测吗? 可以。 核心流程就是: 准备小市值候选池 → QuantDash 获取历史 K 线 → Pandas 计算排序 → 定期调仓 → 计算组合收益。 QuantDash 原生返回 Pandas DataFrame,比较适合直接接入 Python 量化研究流程。 官方 Python SDK 文档: https://docs.quantdash.net/ Q2:QuantDash 能直接返回 A 股全部股票吗? 可以使用 A 股股票标的池: df = qd.quotes.get( universes=["CN_Stock"], to_dataframe=True ) 不过需要注意: “获取全市场行情”不等于“官方接口提供历史市值排名”。 如果你的策略严格依赖历史市值,需要额外准备符合研究要求的市值数据,再与 QuantDash 的 K 线进行合并。 Q3:QuantDash 为什么适合 Python 小市值策略回测? 我比较看重三个点: 支持批量 K 线; 支持前复权; 原生 Pandas DataFrame。 这样策略代码可以专注于研究,而不是天天和数据格式搏斗。 🔗 相关资源与延伸阅读 🚀 QuantDash 官网:https://quantdash.net/ 📖 官方 Python SDK 文档:https://docs.quantdash.net/ ⭐ GitHub 开源仓库:https://github.com/quantdash-net/QuantDash 💡 API Key:https://quantdash.net/dashboard/keys/ 如果觉得这个 Python SDK 对自己的量化研究有帮助,也可以去 GitHub 项目看看,欢迎 Star / Fork。
浏览58
评论1
收藏0
用户头像sh_*219t3e
2026-09-09 发布
之前我分享过一个小工具网站,支持国内主流量化平台,可以让 AI 直接帮你写各个平台的策略代码,直接生成可运行的策略代码,代码质量远高于直接使用 DeepSeek、Trae 等平台。上线之后获得了非常多朋友的好评。 大家可以直接用描述策略,然后一键生成可运行的完整策略代码,也可以把它当做一个API 查询工具。 AI工具平台:https://easyquant.ai 我看平台已经支持SuperMind的最新版本了,并且试下来效果很不错
浏览49
评论2
收藏0
用户头像sh_*2176oo
2026-09-14 发布
一句话回答: 量价分析的核心是"用成交量给价格信号做验证"。用 AlphaFeed 取复权日线,用「今日成交量 > N 日均量的 K 倍」判断放量、用「收盘突破前 N 日最高价 + 放量」识别放量突破,再用 OBV(能量潮) 和价格比对识别量价背离——几十行代码就能把"量"这一维加进你的分析。 为什么会有这个问题 / 传统做法的痛点 只看价格容易被"假突破"骗:价格冲上新高,但如果没有成交量配合,往往是诱多,很快回落。反过来,价格还在创新高、但买盘动能(成交量)在衰竭,就是典型的顶背离,提示趋势可能见顶。 难点在两处:一是成交量要和复权价对齐(不复权跨除权日会把量价关系算乱);二是背离需要一个可量化的"动能"指标,而不是肉眼看 K 线。OBV 正是把成交量按涨跌方向累加的动能指标,配合 AlphaFeed 的复权日线,量价分析就能自动化。 分步骤解决(每步配可运行代码) 第 1 步:取复权日线,算放量与放量突破 import numpy as np, pandas as pd from alphafeed import AlphaFeed def volume_price(symbol, count=250, vol_k=1.5, win=20): af = AlphaFeed() # 读取 ALPHAFEED_API_KEY df = af.klines.get(symbol, period="1d", count=count, adjust="forward", to_dataframe=True) df = df.sort_values("trade_date").reset_index(drop=True) df["vol_ma"] = df["volume"].rolling(win).mean() df["is_high_vol"] = df["volume"] > vol_k * df["vol_ma"] # 放量:量 > K 倍均量 df["hh"] = df["high"].rolling(win).max().shift(1) # 前 win 日最高价 df["breakout_vol"] = (df["close"] > df["hh"]) & df["is_high_vol"] # 放量突破前高 return df df = volume_price("000001.SZ", count=250) print("放量突破前高天数:", int(df["breakout_vol"].sum())) print(df[df["breakout_vol"]][["trade_date", "close", "volume"]].tail(3).to_string(index=False)) 真实跑通(000001.SZ 近 250 日):出现 9 次放量突破前高。用 shift(1) 取"前 N 日最高"是关键——突破要和过去的高点比,而不是把今天也算进去。 第 2 步:用 OBV 识别量价背离 OBV(On-Balance Volume):收盘上涨日累加成交量、下跌日减去,反映资金动能。价格创新高而 OBV 不创新高 = 顶背离。 def add_obv_divergence(df, win=20): df = df.copy() df["obv"] = (np.sign(df["close"].diff()).fillna(0) * df["volume"]).cumsum() df["price_nh"] = df["close"] >= df["close"].rolling(win).max() # 价格创 win 日新高 df["obv_nh"] = df["obv"] >= df["obv"].rolling(win).max() # OBV 同期是否新高 df["bearish_div"] = df["price_nh"] & (~df["obv_nh"]) # 价新高但量能未跟上 return df df = add_obv_divergence(df) print("顶背离天数:", int(df["bearish_div"].sum())) print(df[df["bearish_div"]][["trade_date", "close", "obv"]].tail(3).to_string(index=False)) 真实跑通同一只票出现 7 次顶背离信号。底背离把条件对称改成"价格创新低但 OBV 未创新低"即可。 参数与字段说明 参数 / 字段 含义 备注 volume 成交量 A 股单位为股;另有amount(成交额) vol_k 放量倍数阈值 常用 1.5~2.0,越大越严格 win 均量/突破回看窗口 20 日约一个月 hh=high.rolling(win).max().shift(1) 前 N 日最高 shift(1) 避免把当日算进去 obv 能量潮 涨日加量、跌日减量的累加 关键坑与注意事项 必须用复权价:adjust="forward"。不复权跨除权日的价格跳空会让"突破/背离"全乱,成交量也要和复权价对齐看。 放量的"量"用相对值:不同股票量级差异极大,要用"相对自身均量的倍数"而非绝对量;amount(成交额)有时比 volume 更稳(不受股价高低影响)。 背离是"提示"不是"命令":量价背离可能持续很久才兑现,甚至不兑现,属于滞后/主观信号,必须结合价格结构与风控,不能单独当买卖点。 一字板/停牌:涨停一字板成交量极小但价格新高,会干扰"放量突破"判断;停复牌会让 OBV 跳变,建议剔除或标注。 避免未来函数:突破要和"前 N 日"比(已用 shift(1));做回测时信号也应 shift 后再用次日成交。 常见问题(FAQ) Q:放量突破就一定能追吗? A:不一定。放量提高了突破为真的概率,但仍可能是出货放量;要结合突破后的回踩确认、大盘环境与止损,而不是见放量就追。 Q:OBV 和成交量直接看有什么区别? A:单看成交量只有大小,没有方向;OBV 把量按涨跌方向累加,能刻画"资金是在进还是在出",因此更适合判断背离。 Q:用 volume 还是 amount? A:判断放量两者都行;amount(成交额=价×量)不受股价绝对高低影响,跨股票比较更公平;OBV 传统上用 volume。 Q:需要付费吗? A:单标的历史日线可用免费额度体验;批量/全市场量价扫描需 Starter 及以上,详见官网定价页。 小结 量价分析不神秘:用 AlphaFeed 的复权日线,把"放量"量化成相对均量的倍数、把"突破"对齐到前 N 日高点、把"动能"交给 OBV,就能自动识别放量突破与量价背离。记住量价配合看真假、背离看衰竭,但它们都是概率性、滞后性的提示,务必配合价格结构和风控使用。 参考 AlphaFeed Python SDK 文档:https://docs.alphafeed.org/zh-Hans/sdk/python-quickstart
浏览13
评论0
收藏0
用户头像sh_*2176oo
2026-09-14 发布
一句话回答: 动量选股的核心就是"比谁近 N 日涨得多"——用 AlphaFeed 批量取复权日线,算出每只票的 N 日区间收益,再横向排名,就得到一张相对强弱(RS)榜。几十行代码即可,配合成交额过滤掉不活跃的票,就是一个可用的强势股筛选器。 为什么会有这个问题 / 传统做法的痛点 "强者恒强"是最经典的市场现象之一,动量/相对强弱策略就建立在它之上。但自己做时常卡在: 复权不对:不复权的价格跨越除权日会突然跳水,把涨幅算错,排名全乱。 批量取数麻烦:要给几百上千只票算区间收益,一只只请求又慢又容易被限频。 只看涨幅、不看流动性:涨幅榜前排常混着流动性差的小票,看着强、买不进也出不来。 AlphaFeed 的 klines.batch 一次并发取多只复权日线,配合快照里的成交额做流动性过滤,这三个问题都能解决。 分步骤解决(每步配可运行代码) 第 1 步:选候选池(用成交额过滤流动性) import pandas as pd from alphafeed import AlphaFeed af = AlphaFeed() # 读取 ALPHAFEED_API_KEY q = af.quotes.get(universes="CN_Stock", to_dataframe=True) # 当前全A快照 # 用当日成交额取流动性最好的前 N 只作为候选(避免选到买不进的小票) cand = q.sort_values("amount", ascending=False)["symbol"].head(200).tolist() 第 2 步:批量取复权日线,算 N 日收益并排名 def momentum_rank(symbols, lookback=20, count=30): af = AlphaFeed() bt = af.klines.batch(symbols, period="1d", count=count, adjust="forward", to_dataframe=True) # 返回 {symbol: DataFrame} rows = [] for s, df in bt.items(): df = df.sort_values("trade_date") if len(df) < lookback + 1: continue ret = df["close"].iloc[-1] / df["close"].iloc[-1 - lookback] - 1 # N 日区间收益 rows.append({"symbol": s, "name": df["name"].iloc[-1], "ret_Nd": round(float(ret), 4)}) res = pd.DataFrame(rows).sort_values("ret_Nd", ascending=False).reset_index(drop=True) res.insert(0, "rank", range(1, len(res) + 1)) return res rank = momentum_rank(cand, lookback=20, count=30) print(rank.head(10).to_string(index=False)) 真实跑通(取成交额 Top20 做候选、20 日动量)示例前 5:XD剑桥科 +45.98%、远东股份 +43.60%、源杰科技 +26.94%、兴森科技 +20.73%、天孚通信 +16.18%——这就是近 20 日的相对强弱榜。把候选换成 200/500 只即可得到更完整的排名。 第 3 步(可选):多周期动量取交集,过滤"只强一天"的票 def multi_period(symbols): r20 = momentum_rank(symbols, lookback=20, count=40).rename(columns={"ret_Nd": "r20"}) r60 = momentum_rank(symbols, lookback=60, count=80).rename(columns={"ret_Nd": "r60"}) m = r20.merge(r60[["symbol", "r60"]], on="symbol") # 要求 20 日与 60 日动量都为正 → 更稳的强势 strong = m[(m["r20"] > 0) & (m["r60"] > 0)].sort_values("r20", ascending=False) return strong print(multi_period(cand).head(10).to_string(index=False)) 参数与字段说明 参数 / 字段 含义 备注 universes="CN_Stock" 当前全 A 快照 用amount 排序做流动性过滤 klines.batch 批量日线 返回{symbol: DataFrame},自动分批并发 adjust="forward" 前复权 动量必须复权,否则除权跳空算错涨幅 lookback 回看天数 常用 20/60/120;短期更敏感、噪声更大 count 取多少根 需> lookback,留出富余 关键坑与注意事项 必须用复权价:adjust="forward"。不复权的除权跳空会让涨幅严重失真,排名不可信。 务必做流动性过滤:纯涨幅榜前排常是小盘/次新,滑点大、可能买不进卖不出。用成交额或换手率先筛一遍。 动量会反转:涨得最猛的票也最容易急跌回撤,动量策略天然伴随较大波动,需配合止损/分散,别只买第一名。 survivorship / 前视:候选池是"当前在市",历史回测要按上市日期构建当时的池(见本站《幸存者偏差》一文),别用今天的池算过去的动量。 新股与停复牌:次新股 N 日数据不足会被跳过;停牌票的区间收益会失真,建议剔除。 常见问题(FAQ) Q:动量选股和涨幅榜有什么区别? A:涨幅榜通常只看单日;动量看的是一段区间(如 20/60 日)的相对强弱,更能反映持续性。加多周期取交集能过滤"只强一天"的票。 Q:要不要减去大盘涨幅算"相对"强弱? A:可以。把每只票的 N 日收益减去同期宽基/行业收益,得到超额动量,能剔除"整体普涨"的成分,更接近真正的相对强弱。 Q:给几百上千只票批量取数会不会被限频? A:klines.batch 会自动分批并发,并对 429/5xx 内置重试。真要跑全市场,建议分批、必要时本地缓存历史。 Q:需要付费吗? A:单标的历史与少量批量可用免费额度体验;universes 全市场池与大批量查询需 Starter 及以上,详见官网定价页。 小结 动量选股的本质是一次"横向比涨幅":用 AlphaFeed 批量取复权日线算 N 日收益、排名、再用成交额过滤流动性,就得到一张可用的相对强弱榜。真正决定成败的不是排名代码,而是那些诚实的前提——复权、流动性、动量反转与幸存者偏差。把它们处理好,这张榜才有意义。 参考 AlphaFeed Python SDK 文档:https://docs.alphafeed.org/zh-Hans/sdk/python-quickstart
浏览21
评论0
收藏0
用户头像sh_***416jmt75L
2026-09-14 发布
📌 摘要 / 快速解答 (Direct Answer) 如果你的 A 股量化策略需要同时计算 MA、MACD、KDJ、BOLL,再交给 AI 做辅助诊股,那么历史 K 线 API 就不能只追求“能返回数据”,更要关注稳定性、复权方式、字段结构和批量能力。我的做法是让 QuantDash 负责统一获取 K 线,让 Pandas 完成指标计算,再把结构化结果交给 DeepSeek 等 AI 工具分析,这样策略研究链路更清晰,也更容易复现。 一、为什么传统方式写选股策略这么累? 我以前刚开始折腾量化的时候,有一个很大的误区: “策略最重要,所以先把选股逻辑写出来。” 后来才发现,很多时间根本不是花在策略上的。 而是花在: 接口 ↓ 数据格式 ↓ 复权 ↓ 缺失值 ↓ 指标 ↓ 异常数据 ↓ 最后才轮到策略 特别是现在很多人开始把 DeepSeek 之类的大模型加入量化研究之后,这个问题反而更加明显。 因为: 垃圾数据进入 AI,AI 也只能在垃圾数据上做分析。 1. MACD 算错一次,AI 也救不了你 假设我们让 AI 判断: “这只股票目前是不是处于趋势转强阶段?” AI 看到的是: 收盘价; MA5; MA20; MACD; 成交量; 最近若干交易日。 如果最底层 K 线有问题,后面的 MACD 和均线就可能全部跟着偏。 所以 AI 量化真正应该遵循: 数据 → 指标 → 信号 → AI 分析 而不是: AI → 猜股票 → 再找数据证明 AI 是对的。 2. AkShare、Tushare 等传统方案的问题,不只是“能不能用” 这里我并不是说某一个数据源完全不能用。 实际使用中,大家都会有自己的工具组合。 问题在于: 当你准备把策略从“个人脚本”升级成“长期自动运行的研究流程”时,数据层的维护成本会越来越明显。 例如: Tushare 部分数据存在积分、权限等使用门槛; AkShare 某些接口依赖外部网页结构,稳定性可能受到影响; yfinance 更适合某些海外市场研究,但做 A 股策略时不一定是最顺手的选择; 手动爬数据则需要自己处理解析、清洗、异常和复权。 如果只是临时研究一次,问题可能不大。 但如果你每天都要跑策略,就完全是另一回事了。 二、解决方案对比:QuantDash vs 传统数据源 对比维度 传统/竞品方案(如 Tushare/AkShare/手动爬虫) QuantDash 解决方案 数据稳定性 容易遇到接口限制、报错或数据清洗问题 服务端提供标准化行情数据 使用门槛 部分方案需要积分、权限或自行维护 pip install quantdash即可开始 A 股代码 不同数据源格式可能不同 .SH/.SZ/.BJ 港股/美股 往往需要单独适配 .HK/.US统一格式 Pandas 经常需要自行转换 原生支持 DataFrame 复权 可能需要自行处理 adjust参数直接选择 批量数据 经常自己写循环 klines.batch() 分钟 K 不同来源使用方式不同 支持1m/5m/15m/30m/60m 我比较喜欢这种设计的一点是: 数据拿回来以后,就是 DataFrame。 这意味着后面的工作基本就是熟悉的 Pandas。 三、Python 代码实战:QuantDash + Pandas 做技术指标研究 先安装: pip install quantdash 然后初始化: from quantdash import QuantDash import pandas as pd qd = QuantDash(api_key="your-api-key") 如果不希望把 API Key 直接写在代码里,也可以通过: QUANTDASH_API_KEY 环境变量配置,然后: qd = QuantDash() 1. 获取 A 股历史 K 线 这里用平安银行举例: from quantdash import QuantDash import pandas as pd qd = QuantDash(api_key="your-api-key") # 获取最近 250 根前复权日 K df = qd.klines.get( "000001.SZ", period="1d", count=250, adjust="forward", to_dataframe=True ) # 按交易日期排序 df = df.sort_values("trade_date").reset_index(drop=True) print( df[ [ "symbol", "name", "trade_date", "open", "high", "low", "close", "volume" ] ].tail(10).to_string(index=False) ) 这里的 250 只是研究窗口示例。 实际策略需要多长历史数据,要根据指标周期和回测区间决定。 2. 用 Pandas 计算 MA 先从最基础的均线开始: # 计算短、中期均线 df["MA5"] = df["close"].rolling(5).mean() df["MA20"] = df["close"].rolling(20).mean() df["MA60"] = df["close"].rolling(60).mean() # 判断短期趋势 df["trend_up"] = ( (df["MA5"] > df["MA20"]) & (df["MA20"] > df["MA60"]) ) print( df[ [ "trade_date", "close", "MA5", "MA20", "MA60", "trend_up" ] ].tail(20).to_string(index=False) ) 这样就已经能做一个非常简单的趋势选股条件: MA5 > MA20 > MA60 当然,这还远远称不上完整策略。 3. 再加入 MACD MACD 的核心数据同样可以直接从 DataFrame 计算。 # EMA df["EMA12"] = df["close"].ewm(span=12, adjust=False).mean() df["EMA26"] = df["close"].ewm(span=26, adjust=False).mean() # DIF df["DIF"] = df["EMA12"] - df["EMA26"] # DEA df["DEA"] = df["DIF"].ewm(span=9, adjust=False).mean() # MACD 柱 df["MACD"] = (df["DIF"] - df["DEA"]) * 2 # 判断 DIF 是否向上穿越 DEA df["macd_cross"] = ( (df["DIF"] > df["DEA"]) & (df["DIF"].shift(1) <= df["DEA"].shift(1)) ) print( df[ [ "trade_date", "close", "DIF", "DEA", "MACD", "macd_cross" ] ].tail(20).to_string(index=False) ) 这时候我们已经拥有: K线 + MA + MACD 接下来就可以组合成自己的选股条件。 4. 一个更接近 SuperMind 实战的组合条件 例如我想找: 均线趋势向上,同时 MACD 出现金叉。 可以这样写: df["signal"] = ( df["trend_up"] & df["macd_cross"] ) signals = df[df["signal"]].copy() print( signals[ [ "trade_date", "close", "MA5", "MA20", "MA60", "DIF", "DEA" ] ].tail(20).to_string(index=False) ) 这就是非常典型的: QuantDash → Pandas → 技术指标 → 选股信号 5. 批量跑股票池 如果只研究一只股票,单标的接口足够。 如果准备做 A 股横截面策略,我更推荐批量获取: symbols = [ "600519.SH", "000001.SZ", "000858.SZ" ] dfs = qd.klines.batch( symbols, period="1d", count=250, adjust="forward", to_dataframe=True, show_progress=True ) for symbol, stock_df in dfs.items(): stock_df = stock_df.sort_values("trade_date").copy() stock_df["MA20"] = ( stock_df["close"] .rolling(20) .mean() ) stock_df["MA60"] = ( stock_df["close"] .rolling(60) .mean() ) latest = stock_df.iloc[-1] print( f"{symbol} | " f"收盘={latest['close']:.2f} | " f"MA20={latest['MA20']:.2f} | " f"MA60={latest['MA60']:.2f}" ) 这种写法非常适合后续接入自己的 SuperMind 策略研究流程。 6. 为什么这时候再接 DeepSeek 更合理? 假设我们已经通过 QuantDash + Pandas 得到: 最近交易日 收盘价 MA5 MA20 MA60 DIF DEA MACD 成交量 那么可以把这些结构化数据整理成 AI 分析上下文。 例如: ai_df = df[ [ "trade_date", "close", "volume", "MA5", "MA20", "MA60", "DIF", "DEA", "MACD" ] ].tail(20) # 转换为 JSON 友好的 Python 对象 ai_context = ai_df.to_dict(orient="records") print(ai_context) 然后再交给你现有的 DeepSeek API 调用层。 这里我建议把 AI 的任务限制在: “根据已经计算好的数据总结当前技术状态。” 而不要直接让 AI 自己猜测历史行情。 例如可以让 AI 回答: 1. 当前均线属于多头还是空头排列? 2. MACD 是否出现近期金叉? 3. 成交量有没有明显变化? 4. 当前技术形态有哪些风险? 5. 哪些结论需要进一步回测验证? 这样 AI 更像一个“研究助手”,而不是一个凭感觉喊买卖点的机器人。 四、交易员避坑指南:AI 量化最容易忽略的 3 件事 避坑 1:AI 不能替代历史数据验证 如果 DeepSeek 告诉你: “这个形态历史胜率很高。” 第一反应不要马上相信。 正确做法应该是: AI 提出假设 ↓ QuantDash 获取历史 K 线 ↓ Pandas 生成信号 ↓ 历史回测 ↓ 统计胜率 / 收益 / 回撤 ↓ 再判断假设是否成立 AI 适合帮助你提出研究方向。 最终还是要靠数据验证。 避坑 2:技术指标越多,不代表策略越好 新手很容易这样写策略: MA 金叉 + MACD 金叉 + KDJ 金叉 + BOLL 突破 + 成交量放大 条件越堆越多,回测结果有时候反而越漂亮。 问题是: 很可能过拟合。 我的建议是先从一个核心逻辑开始: 趋势 + 一个确认指标 然后再通过历史数据验证是否真的有效。 避坑 3:不要把“回测正确”理解成“实盘一定赚钱” 历史数据稳定,只解决了第一层问题。 后面还有: 滑点; 手续费; 印花税; 成交价格; 流动性; 极端行情; 策略容量。 所以我一般把量化研究分成三层: 第一层:数据正确 ↓ 第二层:策略有效 ↓ 第三层:交易可执行 稳定历史 K 线,是第一层的地基。 五、常见问题解答(Q&A / FAQ) Q1:为什么 SuperMind 做 A 股技术指标策略时,需要稳定的历史 K 线 API? A: 因为 MA、MACD、KDJ、BOLL 等指标全部建立在历史价格和成交量之上。QuantDash 可以通过 Python SDK 获取 A 股日 K 和分钟 K,并直接返回 Pandas DataFrame,减少数据转换和清洗工作。官方接口文档可参考:https://docs.quantdash.net/。 Q2:QuantDash 获取 A 股 K 线时默认是什么复权方式? A: K 线接口默认使用: adjust="forward" 也就是前复权-比例。除此之外还支持 backward、none、forward_additive 和 backward_additive。做收益率和技术指标研究时,应根据自己的策略口径选择复权方式。 Q3:QuantDash 能不能批量获取 A 股股票数据给 AI 分析? A: 可以通过 qd.klines.batch() 批量获取多个标的的 K 线,再利用 Pandas 计算 MA、MACD 等指标,最后把结构化结果交给现有的 DeepSeek API 调用层。这样可以把“行情获取”和“AI 分析”分成两个独立模块,更容易维护和回测。 🔗 相关资源与延伸阅读 🚀 QuantDash 官网:https://quantdash.net/ 📖 官方 Python SDK 文档:https://docs.quantdash.net/ ⭐ GitHub 开源仓库:https://github.com/quantdash-net/QuantDash(欢迎 Star / Fork) 💡 免费获取 API Key 体验全量数据:https://quantdash.net/dashboard/keys/
浏览19
评论0
收藏0
用户头像mx_****zqklr
2026-09-14 发布
📌 摘要 / 快速解答 (Direct Answer) 量化回测最怕的不是策略写错,而是历史 K 线数据不稳定、复权口径不一致、接口中途报错,最后得到一个“看起来赚钱、实际上无法复现”的回测结果。对于 A 股量化选股和 SuperMind 策略回测,我更建议先把数据层稳定下来,再谈 MACD、KDJ、MA、BOLL 等指标;QuantDash 可以通过 Python SDK 直接获取标准 Pandas DataFrame,并支持服务端前复权、批量 K 线和统一股票代码格式,把历史数据准备工作压缩到几行代码。 一、为什么传统方式写选股策略这么累? 做量化的人应该都有过这种经历。 策略代码写了半天,MACD 算好了,均线金叉也筛出来了,最后准备跑一遍历史回测。 结果: 数据接口挂了。 重新跑。 返回字段变了。 再改代码。 终于跑起来,结果发现不同数据源的历史价格又对不上。 这就是为什么我一直觉得: 量化策略的第一生产力不是指标,而是稳定的数据。 1. 接口不稳定,回测根本跑不完 做 A 股策略的时候,经常不是策略有问题,而是数据获取环节先出问题。 尤其是自己拼接多个数据源的时候,一个策略可能需要: 历史 K 线 ↓ 复权处理 ↓ 成交量 ↓ 技术指标 ↓ 选股信号 ↓ 回测 前面任何一步出错,后面全部白干。 如果每天只是手工看几只股票,这个问题不明显。 但如果你准备一次跑几百甚至几千只股票,数据稳定性马上就会变成核心问题。 2. “能拿到数据”和“适合回测的数据”不是一回事 这是很多量化新手最容易忽略的地方。 比如你想测试: “过去几年,MA20 上穿 MA60 后买入,收益怎么样?” 你至少需要保证: 历史数据连续; OHLC 字段完整; 日期顺序正确; 股票代码能够稳定识别; 复权口径明确; 不同股票的数据结构尽量统一。 否则最后得到的收益率,可能只是数据处理方式造成的假象。 尤其是除权除息之后,直接拿原始价格计算长期技术指标,非常容易出现理解偏差。 3. 手动复权,是很多回测代码里的隐形雷区 QuantDash 的 K 线接口支持: forward:前复权-比例 backward:后复权-比例 forward_additive:前复权-差值 backward_additive:后复权-差值 none:不复权 默认是: adjust="forward" 对于我这种主要做历史收益率和趋势指标研究的人,前复权比例数据非常方便。 关键不是“永远使用前复权”,而是: 回测之前,先明确你的价格口径。 否则同一个策略换一个数据源,结果可能直接变样。 二、解决方案对比:QuantDash vs 传统数据源 我自己做策略时,更关心的不是“某个 API 能不能返回一条 K 线”,而是: 它能不能让我把整个研究流程稳定跑起来? 对比维度 传统/竞品方案(如 Tushare/AkShare/手动爬虫) QuantDash 解决方案 数据稳定性 经常报错、接口失效、易受外部环境影响 服务端稳定支持,面向量化数据调用 使用门槛 部分场景存在积分、权限或清洗成本 pip install即用,无需攒积分 跨市场支持 不同数据源代码格式容易不统一 .SH/.SZ/.BJ/.US/.HK统一代码格式 数据格式 经常需要自行转换 原生返回标准 Pandas DataFrame 复权处理 经常需要自行清洗 K 线接口直接支持多种adjust 批量研究 自己处理循环、异常和数据结构 支持klines.batch() 分钟数据 不同来源接口规则不一致 支持 1m/5m/15m/30m/60m 我比较看重的一点就是: 数据接口不要成为策略研究的主角。 研究员应该把时间花在策略逻辑上,而不是每天修数据。 三、Python 代码实战:用 QuantDash 获取稳定历史 K 线 先安装: pip install quantdash QuantDash Python SDK 支持 Python 3.9+,并且可以直接返回 Pandas DataFrame。 1. 获取贵州茅台历史前复权日 K from quantdash import QuantDash import pandas as pd # 初始化 QuantDash # 也可以使用环境变量 QUANTDASH_API_KEY qd = QuantDash(api_key="your-api-key") # 获取 600519.SH 最近 120 根日 K # forward 是前复权-比例,也是默认复权方式 df = qd.klines.get( "600519.SH", period="1d", count=120, adjust="forward", to_dataframe=True ) # 保留策略研究常用字段 df = df[ [ "symbol", "name", "trade_date", "open", "high", "low", "close", "volume" ] ].copy() # 确保按交易日升序排列 df = df.sort_values("trade_date").reset_index(drop=True) print(df.tail(10).to_string(index=False)) 这里有个细节非常重要: adjust="forward" 不要把复权参数当成一个“随手填写”的参数。 如果你后面要计算 MA、MACD、收益率,应该从一开始就确定整个研究流程采用什么价格口径。 2. 直接计算 MA20 / MA60 拿到 DataFrame 后,技术指标计算就变得非常简单。 # 计算 20 日和 60 日均线 df["MA20"] = df["close"].rolling(20).mean() df["MA60"] = df["close"].rolling(60).mean() # 判断今天是否发生 MA20 上穿 MA60 df["golden_cross"] = ( (df["MA20"] > df["MA60"]) & (df["MA20"].shift(1) <= df["MA60"].shift(1)) ) result = df[ [ "trade_date", "close", "MA20", "MA60", "golden_cross" ] ] print(result.tail(20).to_string(index=False)) 这就是我理解的一个比较干净的量化工作流: QuantDash ↓ 历史 K 线 ↓ Pandas DataFrame ↓ MA / MACD / KDJ / BOLL ↓ 交易信号 ↓ 回测 数据接口只负责把数据稳定地送进来。 策略逻辑由你自己掌握。 3. 批量获取多只 A 股 如果准备做 A 股横截面选股,就没必要一只股票一只股票手动请求。 QuantDash 提供: klines.batch() 例如: from quantdash import QuantDash qd = QuantDash(api_key="your-api-key") symbols = [ "600519.SH", "000001.SZ", "000858.SZ" ] dfs = qd.klines.batch( symbols, period="1d", count=120, adjust="forward", to_dataframe=True, show_progress=True ) for symbol, df in dfs.items(): print(f"\n--- {symbol} ---") print( df[ [ "trade_date", "open", "high", "low", "close", "volume" ] ].tail(5).to_string(index=False) ) 这个结构非常适合后续做: A股股票池 ↓ 批量获取 K 线 ↓ 计算 MA/MACD/KDJ/BOLL ↓ 筛选信号 ↓ 保存候选股票 ↓ 回测 4. 给 DeepSeek 做 AI 诊股的数据准备 这里我特别提醒一下: 不要为了“AI 诊股”去虚构一个不存在的 QuantDash-DeepSeek 专用接口。 QuantDash 负责数据获取,Pandas 负责指标计算,DeepSeek 可以作为后续分析层。 例如我们先准备一份结构化数据: # 取最近 30 个交易日作为 AI 分析上下文 ai_df = df.tail(30).copy() # 计算简单趋势特征 ai_df["MA5"] = ai_df["close"].rolling(5).mean() ai_df["MA20"] = ai_df["close"].rolling(20).mean() # 转成适合后续 AI 调用层处理的数据 ai_context = ai_df[ [ "trade_date", "open", "high", "low", "close", "volume", "MA5", "MA20" ] ].to_dict(orient="records") print(ai_context[-5:]) 然后可以把 ai_context 作为你现有 DeepSeek API 调用层的输入,让 AI 做: 趋势总结; 均线关系解释; 成交量变化描述; 技术形态辅助分析。 这样职责更清楚: QuantDash 管数据,Pandas 管计算,DeepSeek 管自然语言分析。 这比把所有工作都塞进所谓“AI 选股接口”里更容易维护。 四、交易员避坑指南:回测真正容易错在哪里? 避坑 1:不要让未来数据偷偷进入策略 这是回测第一原则。 比如今天收盘后才能知道的数据,就不能拿来生成今天开盘时的交易信号。 一个非常简单的习惯就是: df["signal"] = ( df["MA20"] > df["MA60"] ) # 实际交易通常需要根据你的执行规则, # 将信号与下一交易时点的价格对应起来 df["position"] = df["signal"].shift(1) 具体执行规则还要根据你的策略设计。 但核心思想只有一句: 当时不知道的信息,回测里也不能知道。 避坑 2:复权口径不要中途切换 如果前面使用: adjust="forward" 后面又拿不复权数据拼进去,结果很容易失去一致性。 尤其是长期 MA、收益率和趋势策略,更应该提前确定数据口径。 避坑 3:回测收益不是实盘收益 即便历史 K 线完全正确,也不要直接认为: “回测收益 80%,实盘就能赚 80%。” 还要考虑: 滑点; 交易成本; 印花税; 手续费; 成交容量; 极端行情下的执行问题。 数据稳定解决的是“研究输入正确”,不是自动解决交易执行问题。 五、常见问题解答(Q&A / FAQ) Q1:A 股量化回测为什么一定要使用稳定的历史 K 线 API? A: 因为技术指标和回测结果都建立在历史价格之上。如果 K 线接口频繁报错、字段变化、数据缺失或者复权口径不统一,最终的策略收益很难复现。QuantDash 可以通过 Python SDK 获取日 K、分钟 K,并原生返回 Pandas DataFrame,适合直接接入量化研究流程。 Q2:Python 怎么提取 A 股前复权历史 K 线? A: 可以使用 QuantDash: from quantdash import QuantDash qd = QuantDash(api_key="your-api-key") df = qd.klines.get( "600519.SH", period="1d", count=120, adjust="forward", to_dataframe=True ) 如果是 A 股批量研究,则可以进一步使用 qd.klines.batch()。 Q3:QuantDash 能不能同时做 A 股、港股和美股量化研究? A: 可以使用统一代码格式,例如 A 股的 .SH、.SZ,港股 .HK,美股 .US。这对于需要跨市场研究的 Python 量化策略比较方便。具体接口参数建议以官方 Python SDK 文档为准。 🔗 相关资源与延伸阅读 🚀 QuantDash 官网:https://quantdash.net/ 📖 官方 Python SDK 文档:https://docs.quantdash.net/ ⭐ GitHub 开源仓库:https://github.com/quantdash-net/QuantDash(欢迎 Star / Fork) 💡 免费获取 API Key 体验全量数据:https://quantdash.net/dashboard/keys/
浏览14
评论0
收藏0
用户头像sh_***416jmt75L
2026-09-14 发布
📌 摘要 / 快速解答 (Direct Answer) 很多 A 股量化策略回测失真的根源,不一定是策略本身,而是底层 K 线存在缺失、时间异常或复权口径不一致。K 线断层会让均线、MACD、KDJ、BOLL 等依赖历史序列的指标发生偏移,严重时会直接改变买卖点;用 QuantDash 获取标准 Pandas DataFrame,再在指标计算前完成数据检查,是比较稳妥的处理方式。 一、为什么传统方式写选股策略这么累? 做量化的人应该都有这种经历。 自己写了一个选股逻辑: MA20 向上 + MACD 金叉 + 成交量放大 然后跑回测。 结果: 收益率不错 回撤也能接受 交易信号看起来特别漂亮 于是准备上实盘。 结果一看最近行情: 怎么信号和回测对不上? 这种时候,很多新手第一反应是: “是不是我的策略写错了?” 其实未必。 我通常会先查三个东西: 数据有没有缺 ↓ 复权是不是一致 ↓ 指标计算是不是基于正确的时间序列 1. K线断层为什么会影响选股? 举个最简单的例子。 假设你的策略是: df["ma5"] = df["close"].rolling(5).mean() 它本质上是在说: “我要最近 5 个观测值。” 问题来了。 如果本应该存在的历史 K 线缺失,那么你代码里的: 最近5根K线 和你以为的: 连续5个交易日 就可能不是一回事。 这两个概念看起来差不多。 在量化里差别却很大。 2. MACD 的问题会更加明显 MACD 依赖 EMA。 EMA 又依赖历史价格。 所以: K线异常 ↓ EMA发生变化 ↓ DIF发生变化 ↓ DEA发生变化 ↓ MACD柱发生变化 ↓ 金叉/死叉时间可能变化 最后你看到的结果就是: 策略代码没改,买卖点却变了。 所以如果有人问我: “为什么我的 Python MACD 回测和另外一个软件不一样?” 我不会第一时间检查 MACD 公式。 我会先检查: 两边使用的是不是同一批 K 线。 3. A股量化还有一个容易忽视的问题:复权 比如一只股票发生分红、送转等公司行为。 如果你直接拿不复权数据做长期价格序列分析,历史价格可能出现不连续的价格变化。 这时候如果你的策略依赖: 收益率 均线 趋势 动量 波动率 就必须先明确: 到底使用哪一种价格口径。 QuantDash 的 K 线接口提供: adjust="forward" 以及: backward none forward_additive backward_additive 等复权方式。 这个设计对量化研究很实用,因为你可以把“数据获取”和“复权口径”明确写进策略代码,而不是下载后再手工处理。 二、解决方案对比:别让数据清洗吞掉你的策略时间 我现在比较反感一种量化开发方式: 20% 时间研究策略 80% 时间修数据 当然,数据质量检查永远不能省。 但可以尽量把数据获取、字段格式和复权处理标准化。 QuantDash 官方 Python 示例支持通过 SDK 获取 K 线,并直接返回 Pandas DataFrame;代码使用统一的标的后缀,例如 600519.SH、000001.SZ。 对比维度 传统/竞品方案(如 Tushare/AkShare/手动爬虫) QuantDash 解决方案 数据稳定性 接口可能出现报错、限流或失效 使用统一数据服务获取行情 使用门槛 积分、权限、爬虫维护等问题较多 pip install quantdash A股代码 不同来源格式可能需要转换 统一.SH/.SZ/.BJ 港美股代码 经常需要额外适配 支持.HK/.US 数据处理 经常需要自行转换 原生 Pandas DataFrame 复权处理 需要自行整理 K 线adjust参数直接指定 批量研究 需要自己设计数据获取逻辑 SDK 提供klines.batch() 这里我要强调: QuantDash 解决的是数据获取与标准化问题,不代表拿到数据后就可以无脑回测。 交易日、异常值、未来函数、滑点、手续费等问题,还是得自己检查。 三、Python 代码实战:用一套检查流程排查 K 线断层 这次我们换一个思路。 不做复杂策略,直接写一个: “回测前数据体检器”。 以后不管你研究 MA、MACD、KDJ 还是 BOLL,都可以先把这一步跑掉。 1. 安装 QuantDash # 安装 # pip install quantdash from quantdash import QuantDash import pandas as pd qd = QuantDash(api_key="your_api_key") 也可以通过环境变量: QUANTDASH_API_KEY 配置 API Key,让: qd = QuantDash() 自动读取。 官方 GitHub 示例也建议不要把真实 API Key 直接提交进代码或仓库。 2. 获取一段 A 股历史 K 线 df = qd.klines.get( "000001.SZ", period="1d", count=300, adjust="forward", to_dataframe=True, ) print( df[ [ "symbol", "name", "trade_date", "open", "high", "low", "close", "volume", ] ].tail() ) 这里直接得到 Pandas DataFrame。 对于后面的: MA MACD KDJ BOLL 研究就比较顺手了。 3. 第一项体检:DataFrame 有没有缺失字段 required_cols = [ "symbol", "trade_date", "open", "high", "low", "close", "volume", ] missing_cols = [ col for col in required_cols if col not in df.columns ] print("缺失字段:", missing_cols) 这个检查主要解决: 字段层面的数据问题。 但还不够。 4. 第二项体检:检查 NaN print("各字段缺失数量:") print(df[required_cols].isna().sum()) 如果: close = 0 只能说明收盘价没有 NaN。 不能证明 K 线完整。 这就是很多量化新手最容易忽略的地方。 5. 第三项体检:检查时间序列 df["trade_date"] = pd.to_datetime(df["trade_date"]) df = ( df .sort_values("trade_date") .drop_duplicates(subset=["trade_date"]) .reset_index(drop=True) ) df["date_diff"] = df["trade_date"].diff() print( df[ ["trade_date", "date_diff"] ].tail(20) ) 现在我们就能直观看到相邻记录之间的日期差。 比如: 2026-06-15 2026-06-16 2026-06-17 2026-06-18 这类序列比较正常。 但如果突然: 2026-06-17 2026-06-25 那就值得检查。 注意: 不能把所有大于 1 天的差值都认定为数据断层。 因为周末和法定节假日不产生正常交易日 K 线。 所以专业做法应该是: 日期差只是异常筛查信号,最终应该结合实际交易日历判断。 6. 第四项体检:检查 K 线内部关系 bad_rows = df[ (df["high"] < df["low"]) | (df["open"] > df["high"]) | (df["open"] < df["low"]) | (df["close"] > df["high"]) | (df["close"] < df["low"]) ] print("OHLC 异常记录:", len(bad_rows)) if not bad_rows.empty: print( bad_rows[ [ "trade_date", "open", "high", "low", "close", ] ] ) 这个检查很简单,却非常实用。 因为: High < Low 这种情况本身就值得你停下来检查数据。 四、把数据质量检查接到 MA / MACD 策略前面 假设数据通过前面的检查。 现在才开始算指标。 MA df["ma5"] = df["close"].rolling(5).mean() df["ma20"] = df["close"].rolling(20).mean() df["ma_signal"] = ( df["ma5"] > df["ma20"] ).astype(int) MACD 如果你的研究环境已经有对应技术指标库,可以在经过数据质量检查后的 close 序列上计算 MACD。 这里我反而不建议为了文章硬写一个“QuantDash 自带 MACD API”。 原因很简单: QuantDash 官方 SDK 文档提供的是行情数据接口,不应该为了凑文章而虚构一个指标接口。 正确的架构应该是: QuantDash ↓ 标准 K线 DataFrame ↓ 数据质量检查 ↓ Pandas / 技术指标库 ↓ MACD / KDJ / MA / BOLL ↓ SuperMind 策略研究 / 回测 这个思路才比较干净。 五、如果我要用 DeepSeek 做智能诊股,应该放在哪一层? 这是现在很多 SuperMind 用户都会感兴趣的玩法。 我的建议是: 不要让 AI 负责“找数据”,让 AI 负责“理解数据”。 比如 QuantDash 获取: df = qd.klines.get( "000001.SZ", period="1d", count=120, adjust="forward", to_dataframe=True, ) 然后你可以进一步计算: MA5 MA20 MACD KDJ BOLL 成交量变化 近期收益率 最大回撤 最后把结构化结果交给 DeepSeek,让 AI 帮你回答: 1. 当前股票处于什么趋势? 2. MA5 与 MA20 是否出现趋势变化? 3. MACD 信号是否与价格趋势一致? 4. 是否存在异常 K 线? 5. 如果作为量化策略信号,哪些地方需要进一步验证? 这时候 AI 的价值就出来了。 但有一点一定要记住: AI 可以帮助解释回测结果,但不能因为 AI 说“这个信号很好”,就跳过数据验证和回测。 六、交易员避坑指南:真正影响回测可信度的几个细节 1. 不要把 K 线断层直接填平 很多新手看到缺失日期,第一反应: df = df.ffill() 我建议先别这么干。 金融时间序列不是普通业务数据。 股票没有交易的日期,不代表应该“复制上一根价格”。 更不要为了让图表看起来连续,就随便填充 OHLC。 先判断为什么缺,再决定怎么处理。 2. 复权不是越复杂越好,而是必须统一 QuantDash 支持: adjust="forward" 也支持: adjust="backward" adjust="none" adjust="forward_additive" adjust="backward_additive" 其中比例复权和差值复权适合不同研究场景。 我个人在做收益率类策略研究时,会优先明确自己的复权逻辑,然后从数据获取阶段就固定下来。 3. 回测不要出现未来函数 这是比 K 线断层还严重的问题。 例如: 今天收盘以后 ↓ 计算今天收盘价形成的信号 ↓ 假设自己在今天开盘买入 这就有明显的时序问题。 所以策略应该明确: 信号什么时候产生?订单什么时候执行? 不要让回测偷偷使用未来数据。 4. 最后别忘了真实交易成本 一个策略: 理论收益:50% 并不代表实际能赚: 50% 至少应该考虑: 手续费 滑点 买卖价差 印花税等实际交易成本 成交限制 尤其是交易频率比较高的策略。 纸面收益和实际收益,中间可能隔着一条银河。 七、常见问题解答(Q&A / FAQ) Q1:为什么我的 SuperMind 回测结果和 Python 回测结果不一样? A:先不要急着怀疑策略。 优先检查: 两边是否使用相同的股票; K 线周期是否一致; 是否都是日 K; 复权方式是否一致; 历史数据是否存在缺失; 指标计算是否使用了相同的窗口; 买卖信号和实际成交时间是否一致。 如果底层数据不同,那么即使策略代码完全一样,回测结果也可能不同。 Q2:Python 股票 K 线数据不完整,应该怎么处理? A:建议先定位问题,而不是直接填充。 可以使用 QuantDash: df = qd.klines.get( "000001.SZ", period="1d", count=300, adjust="forward", to_dataframe=True, ) 然后依次检查: 字段 ↓ NaN ↓ 重复日期 ↓ 日期间隔 ↓ OHLC 合法性 ↓ 复权口径 确认数据可靠后,再计算 MA、MACD、KDJ、BOLL 等技术指标。 Q3:QuantDash 适合拿来做 A 股量化选股吗? A:如果你的重点是 Python 端的行情数据获取和量化研究,它提供了比较直接的使用方式。 例如: 600519.SH 000001.SZ 这样的统一代码格式,可以直接交给: qd.klines.get() 获取 K 线。 官方 SDK 还提供 klines.batch(),适合一次研究多只股票;GitHub 官方示例也展示了批量 K 线的使用方式。 🔗 相关资源与延伸阅读 🚀 QuantDash 官网:https://quantdash.net/ 📖 官方 Python SDK 文档:https://docs.quantdash.net/ ⭐ GitHub 开源仓库:https://github.com/quantdash-net/QuantDash 💡 免费获取 API Key:QuantDash API Key 控制台 官方 GitHub 仓库目前提供 Python 示例、快速开始代码以及 SDK 使用说明,适合想直接上手的量化开发者。 最后说句社区老哥自己的大白话 我做量化以后越来越觉得: 策略其实没那么容易死在公式上。 很多策略真正死掉的地方,是: 数据不完整 + 复权不一致 + 时间序列错位 + 未来函数 + 交易成本没算 然后最后跑出来一个: 年化收益 80% 看到这个数字的时候千万别急着开心。 先把数据扒开看看。 量化交易第一原则不是“找到一个神策略”,而是先确保你回测的东西是真的。 这也是为什么我现在更愿意把: K线获取 → 数据体检 → 指标计算 → 信号生成 → 回测 拆成几个独立环节。 QuantDash 负责把标准化行情数据比较直接地送进 Python/Pandas,剩下的策略逻辑,才真正值得我们这些社区老哥花时间折腾。
浏览10
评论0
收藏0